7. Passenger
Simple to operate.
Simple configuration.
Handles worker management.
Great for multi-app environments.
Great for low resource environments.
Attached to Nginx/Apache HTTPD.
15. 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.
16. N+1 Query Creep
# app/models/customer.rb
class Customer < ActiveRecord::Base
has_many :addresses
end
# app/models/address.rb
class Address < ActiveRecord::Base
belongs_to :customer
end
# app/controllers/customers_controller.rb
class CustomersController < ApplicationController
def index
@customers = Customer.all
end
end
# app/views/customers/index.html.erb
<% @customers.each do |customer| %>
<%= content_tag :h1, customer.name %>
<% end %>
17. 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 1
SELECT "addresses".* FROM "addresses" WHERE "addresses"."customer_id" = 2 AND
"addresses"."primary" = 't' LIMIT 1
SELECT "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
18. Eager Load Instead
# app/controllers/customers_controller.rb
class CustomersController < ApplicationController
def index
@customers = Customer.includes(:addresses).all
end
end
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)
20. 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.
22. 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
end
end
26. 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.rb
class ProductsController < ActionController
def update
expire_fragment(‘all_products’)
end
end
31. Rescued by Resque
class 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
end
end
32. Get in Line
class 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
33. It’s Free in Rails 4
Establishing basic Queue API.
Implement push and pop.
Easily swap out for Resque, Sidekiq,
Delayed job.
38. 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.