Intro to Rails #webperf
teu rhu man@ ama
ruby -v                1.9.2            1.8.71.9.31.8.6
require ‘benchmark’  MRI 1.9.3              MRI 1.8.7              RBX 1.2.4         JRuby 1.6.7
Passengers,Unicorns,Pumas.Oh my!
PassengerSimple to operate.Simple configuration.Handles worker management.Great for multi-app environments.Great for low r...
UnicornHighly configurable.Independent of front-end web server.Master will reap children on timeout.Great for single app e...
PumaBased on Mongrel.Designed for concurrency.Uses real threads.
Anatomy of a Web Request
Link Clicked                DOM LoadedRedirects Cache DNS TCP SSL Request App Response DOM Render                         ...
80% in Front-End
Inside the Web App
Database Performance
Lazy Loading๏   ORMs make it easy to access data.๏   Easy access to data can create issues.๏   Performance issues are hard...
N+1 Query Creep# app/models/customer.rbclass Customer < ActiveRecord::Base  has_many :addressesend# app/models/address.rbc...
N+1 Query Creep# app/views/customers/index.html.erb<% @customers.each do |customer| %>  <%= content_tag :h1, customer.name...
Eager Load Instead# app/controllers/customers_controller.rbclass CustomersController < ApplicationController  def index   ...
Finding N+1
Missing Indexes๏   Searching a 1,000 row table with an    index is 100x faster than searching    without.๏   Put indexes a...
Missing Indexes Hurt
Indexes are Easy# db/migrate/20120201040247_add_index_for_shop_id_on_orders.rbclass AddIndexForShopIdOnOrders < ActiveReco...
Cache All The Things
Page Caching# app/controllers/products_controller.rbclass ProductsController < ActionController  caches_page :index  def i...
Action Caching# app/controllers/products_controller.rbclass ProductsController < ActionController  before_filter :authenti...
Fragment Caching# app/views/products/index.html.erb<% Order.find_recent.each do |o| %>  <%= o.buyer.name %> bought <%= o.p...
Expiring Caches    is Hard
Russian Doll Caching# app/views/products/show.html.erb<% cache product do %>  Product options:  <%= render product.options...
Background Jobs
ProcrastinateReporting.Sending email.Processing images.Call external services.Building & Expiring Caches.
Rescued by Resqueclass ReferralProcessor  @queue = :referrals_queue  def self.perform(schema_name, order_item_id)    order...
Get in Lineclass ReferralObserver < ActiveRecord::Observer  def after_create(referral)    Resque.enqueue_in(1.day, Referra...
It’s Free in Rails 4Establishing basic Queue API.Implement push and pop.Easily swap out for Resque, Sidekiq,Delayed job.
What’s One More Second?
7%Fewer Conversions
11%Fewer Page Views
Time isMoney
Monitor your applications.Performance is not set it and forget it.Database indexes are cheap, make more.Cache something, s...
Q?30-day free trial atnewrelic.com/30
Intro to-rails-webperf
Upcoming SlideShare
Loading in...5
×

Intro to-rails-webperf

289

Published on

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
289
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
4
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Intro to-rails-webperf

  1. 1. Intro to Rails #webperf
  2. 2. teu rhu man@ ama
  3. 3. ruby -v 1.9.2 1.8.71.9.31.8.6
  4. 4. require ‘benchmark’ MRI 1.9.3 MRI 1.8.7 RBX 1.2.4 JRuby 1.6.7
  5. 5. Passengers,Unicorns,Pumas.Oh my!
  6. 6. PassengerSimple to operate.Simple configuration.Handles worker management.Great for multi-app environments.Great for low resource environments.Attached to Nginx/Apache HTTPD.
  7. 7. UnicornHighly configurable.Independent of front-end web server.Master will reap children on timeout.Great for single app environments.Allows for zero downtime deploys.
  8. 8. PumaBased on Mongrel.Designed for concurrency.Uses real threads.
  9. 9. Anatomy of a Web Request
  10. 10. Link Clicked DOM LoadedRedirects Cache DNS TCP SSL Request App Response DOM Render First Byte Load
  11. 11. 80% in Front-End
  12. 12. Inside the Web App
  13. 13. Database Performance
  14. 14. Lazy Loading๏ ORMs make it easy to access data.๏ Easy access to data can create issues.๏ Performance issues are hard to see in development mode.๏ Look to production metrics to optimize and refactor.
  15. 15. N+1 Query Creep# app/models/customer.rbclass Customer < ActiveRecord::Base has_many :addressesend# app/models/address.rbclass Address < ActiveRecord::Base belongs_to :customerend# app/controllers/customers_controller.rbclass CustomersController < ApplicationController def index @customers = Customer.all endend# app/views/customers/index.html.erb<% @customers.each do |customer| %> <%= content_tag :h1, customer.name %><% end %>
  16. 16. N+1 Query Creep# app/views/customers/index.html.erb<% @customers.each do |customer| %> <%= content_tag :h1, customer.name %> <%= content_tag :h2, customer.addresses.first.city %><% end %>If @customers has 100 records, youll have 101 queries:SELECT "customers".* FROM "customers"SELECT "addresses".* FROM "addresses" WHERE "addresses"."customer_id" = 1 AND "addresses"."primary" = t LIMIT 1SELECT "addresses".* FROM "addresses" WHERE "addresses"."customer_id" = 2 AND "addresses"."primary" = t LIMIT 1SELECT "addresses".* FROM "addresses" WHERE "addresses"."customer_id" = 3 AND "addresses"."primary" = t LIMIT 1......SELECT "addresses".* FROM "addresses" WHERE "addresses"."customer_id" = 100 AND "addresses"."primary" = t LIMIT 1
  17. 17. Eager Load Instead# app/controllers/customers_controller.rbclass CustomersController < ApplicationController def index @customers = Customer.includes(:addresses).all endendIf @customers has 100 records, now we only have 2 queries:SELECT "customers".* FROM "customers"SELECT "addresses".* FROM "addresses" WHERE "addresses"."customer_id" IN (1,2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98,99, 100)
  18. 18. Finding N+1
  19. 19. Missing Indexes๏ Searching a 1,000 row table with an index is 100x faster than searching without.๏ Put indexes anywhere you might need to query; less is not more with indexes.๏ Writing an index will lock your tables.
  20. 20. Missing Indexes Hurt
  21. 21. Indexes are Easy# db/migrate/20120201040247_add_index_for_shop_id_on_orders.rbclass AddIndexForShopIdOnOrders < ActiveRecord::Migration def change add_index :orders, :shop_id endend
  22. 22. Cache All The Things
  23. 23. Page Caching# app/controllers/products_controller.rbclass ProductsController < ActionController caches_page :index def index @products = Products.all end def create expire_page :action => :index endend# /opt/nginx/conf/nginx.conflocation / { gzip_static on;}
  24. 24. Action Caching# app/controllers/products_controller.rbclass ProductsController < ActionController before_filter :authenticate caches_action :index def index @products = Product.all end def create expire_action :action => :index endend
  25. 25. Fragment Caching# app/views/products/index.html.erb<% Order.find_recent.each do |o| %> <%= o.buyer.name %> bought <%= o.product.name %><% end %><% cache(‘all_products’) do %> All available products: <% Product.all.each do |p| %> <%= link_to p.name, product_url(p) %> <% end %><% end %># app/controllers/products_controller.rbclass ProductsController < ActionController def update expire_fragment(‘all_products’) endend
  26. 26. Expiring Caches is Hard
  27. 27. Russian Doll Caching# app/views/products/show.html.erb<% cache product do %> Product options: <%= render product.options %><% end %># app/views/options/_option.html.erb<% cache option do %> <%= option.name %> <%= option.description %><% end %># app/models/product.rbclass Product < ActiveRecord::Base has_many :optionsend# app/models/option.rbclass Option < ActiveRecord::Base belongs_to :product, touch: trueend
  28. 28. Background Jobs
  29. 29. ProcrastinateReporting.Sending email.Processing images.Call external services.Building & Expiring Caches.
  30. 30. Rescued by Resqueclass ReferralProcessor @queue = :referrals_queue def self.perform(schema_name, order_item_id) order_item = OrderItem.find(order_item_id) order = order_item.order user = order.user credit = AccountCredit.credit(order_item.unit_price, user, referral) credit.message = I18n.t(account_credits.predefined_messages.referral, :description => order_item.description) credit.save! debit = Transaction.account_debit(credit.amount, user) debit.order = order debit.save! order.issue_refund(return_to_inventory: false, gateway_first: true, cancel_items: false, cancel_certificates: false, amount: credit.amount, as: original, notify_user: false) if user.receives_mail_for?(:referral_purchase) SystemMailer.referral_refund(order_item, credit).deliver end endend
  31. 31. Get in Lineclass ReferralObserver < ActiveRecord::Observer def after_create(referral) Resque.enqueue_in(1.day, ReferralProcessor, referral.item.id) endend# Get it started$ PIDFILE=./resque.pid BACKGROUND=yes QUEUE=referrals_queue rake environment resque:work
  32. 32. It’s Free in Rails 4Establishing basic Queue API.Implement push and pop.Easily swap out for Resque, Sidekiq,Delayed job.
  33. 33. What’s One More Second?
  34. 34. 7%Fewer Conversions
  35. 35. 11%Fewer Page Views
  36. 36. Time isMoney
  37. 37. Monitor your applications.Performance is not set it and forget it.Database indexes are cheap, make more.Cache something, somewhere.Push work off to the background.Don’t neglect front-end performance.
  38. 38. Q?30-day free trial atnewrelic.com/30
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×