Intro to-rails-webperf

522 views

Published on

Published in: Technology
  • Be the first to comment

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

×