intro to-rails-webperf

39
Intro to Rails #webperf

Upload: new-relic

Post on 19-Jan-2015

246 views

Category:

Technology


0 download

DESCRIPTION

 

TRANSCRIPT

Page 1: Intro to-rails-webperf

Intro to Rails #webperf

Page 2: Intro to-rails-webperf
Page 3: Intro to-rails-webperf

@amateurhuman

Page 4: Intro to-rails-webperf

1.9.2

1.8.7

1.9.3

1.8.6

ruby -v

Page 5: Intro to-rails-webperf

MRI 1.9.3

MRI 1.8.7

require ‘benchmark’

RBX 1.2.4

JRuby 1.6.7

Page 6: Intro to-rails-webperf

Passengers,Unicorns,Pumas.Oh my!

Page 7: Intro to-rails-webperf

PassengerSimple to operate.Simple configuration.Handles worker management.Great for multi-app environments.Great for low resource environments.Attached to Nginx/Apache HTTPD.

Page 8: Intro to-rails-webperf

Unicorn

Highly configurable.Independent of front-end web server.Master will reap children on timeout.Great for single app environments.Allows for zero downtime deploys.

Page 9: Intro to-rails-webperf

Puma

Based on Mongrel.Designed for concurrency.Uses real threads.

Page 10: Intro to-rails-webperf

Anatomy of a Web Request

Page 11: Intro to-rails-webperf

Redirects Cache DNS TCP SSL Request App Response DOM Render

Link Clicked

First Byte

DOM Loaded

Load

Page 12: Intro to-rails-webperf

80% in Front-End

Page 13: Intro to-rails-webperf

Inside the Web App

Page 14: Intro to-rails-webperf

Database Performance

Page 15: Intro to-rails-webperf

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.

Page 16: Intro to-rails-webperf

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 %>

Page 17: Intro to-rails-webperf

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, you'll 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

Page 18: Intro to-rails-webperf

Eager Load Instead# app/controllers/customers_controller.rbclass CustomersController < ApplicationController def index @customers = Customer.includes(:addresses).all endend

If @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)

Page 19: Intro to-rails-webperf

Finding N+1

Page 20: Intro to-rails-webperf

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.

Page 21: Intro to-rails-webperf

Missing Indexes Hurt

Page 22: Intro to-rails-webperf

Indexes are Easy# db/migrate/20120201040247_add_index_for_shop_id_on_orders.rb

class AddIndexForShopIdOnOrders < ActiveRecord::Migration def change add_index :orders, :shop_id endend

Page 23: Intro to-rails-webperf

Cache All The Things

Page 24: Intro to-rails-webperf

Page Caching# app/controllers/products_controller.rbclass ProductsController < ActionController caches_page :index def index @products = Products.all end def create expire_page :action => :index end end

# /opt/nginx/conf/nginx.conflocation / { gzip_static on;}

Page 25: Intro to-rails-webperf

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 end end

Page 26: Intro to-rails-webperf

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’) end

end

Page 27: Intro to-rails-webperf

Expiring Cachesis Hard

Page 28: Intro to-rails-webperf

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

Page 29: Intro to-rails-webperf

Background Jobs

Page 30: Intro to-rails-webperf

ProcrastinateReporting.Sending email.Processing images.Call external services.Building & Expiring Caches.

Page 31: Intro to-rails-webperf

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

Page 32: Intro to-rails-webperf

Get in Lineclass ReferralObserver < ActiveRecord::Observer

def after_create(referral) Resque.enqueue_in(1.day, ReferralProcessor, referral.item.id) end

end

# Get it started$ PIDFILE=./resque.pid \ BACKGROUND=yes \ QUEUE=referrals_queue \ rake environment resque:work

Page 33: Intro to-rails-webperf

It’s Free in Rails 4

Establishing basic Queue API.Implement push and pop.Easily swap out for Resque, Sidekiq, Delayed job.

Page 34: Intro to-rails-webperf

What’s One More Second?

Page 35: Intro to-rails-webperf

7%Fewer Conversions

Page 36: Intro to-rails-webperf

11%Fewer Page Views

Page 37: Intro to-rails-webperf

Time isMoney

Page 38: Intro to-rails-webperf

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.

Page 39: Intro to-rails-webperf

30-day free trial atnewrelic.com/30

Q?