Facepalm to Foolproof
Avoiding Common Production Pitfalls
Jon McCartie - @jmccartie
Slide 2
Slide 2 text
Hi there! ! I’m Jon McCartie.
I work at Heroku. I help people fix things.
Slide 3
Slide 3 text
No content
Slide 4
Slide 4 text
No content
Slide 5
Slide 5 text
No content
Slide 6
Slide 6 text
No content
Slide 7
Slide 7 text
No content
Slide 8
Slide 8 text
AirlessBNB
Vacation Rentals In Outer Space
Slide 9
Slide 9 text
No content
Slide 10
Slide 10 text
$ ab -c 100 -n 500 http://airlessbnb.com/
Requests per second: 41.53 [#/sec] (mean)
Time per request: 2407.916 [ms] (mean)
Time per request: 24.079 [ms]
$ ab -c 100 -n 500 http://airlessbnb.com/
Requests per second: 41.53 [#/sec] (mean)
Time per request: 2407.916 [ms] (mean)
Time per request: 24.079 [ms]
Slide 15
Slide 15 text
$ ab -c 100 -n 500 http://airlessbnb.com/
Requests per second: 82.94 [#/sec] (mean)
Time per request: 1205.721 [ms] (mean)
Time per request: 12.057 [ms]
Slide 16
Slide 16 text
?
Slide 17
Slide 17 text
No content
Slide 18
Slide 18 text
No content
Slide 19
Slide 19 text
Memory Enemies
1. Memory leaks
- https:/
/github.com/schneems/derailed_benchmarks
- https:/
/blog.codeship.com/debugging-a-memory-leak-on-heroku/
2. Too many web processes
- how much memory does your Rails app consume in one web worker?
- how much memory do you have?
Slide 20
Slide 20 text
$ free -m
total used free shared buffers cached
Mem: 8014 7528 486 141 291 4789
-/+ buffers/cache: 2446 5567
Swap: 511 7 504
Watch Your Memory Usage
Slide 21
Slide 21 text
$ free -m
total used free shared buffers cached
Mem: 8014 7528 486 141 291 4789
-/+ buffers/cache: 2446 5567
Swap: 511 7 504
Watch Your Memory Usage
Slide 22
Slide 22 text
$ free -m
total used free shared buffers cached
Mem: 8014 7528 486 141 291 4789
-/+ buffers/cache: 2446 5567
Swap: 511 7 504
Watch Your Memory Usage
Slide 23
Slide 23 text
$ free -m
total used free shared buffers cached
Mem: 8014 7528 486 141 291 4789
-/+ buffers/cache: 2446 5567
Swap: 511 7 504
Watch Your Memory Usage
Slide 24
Slide 24 text
=> ☄
Slide 25
Slide 25 text
404 - Not Found
The Asset Pipeline
Slide 26
Slide 26 text
# this
/app/assets/images/logo.png
# becomes this
/public/assets/logo-a2c31b208b2a62ad9da1374497ad4db1.png
The Asset Pipeline
Slide 27
Slide 27 text
# Nope
# Yup
<%= image_tag "logo.png" %>
Your Views
$ mina deploy
-----> Creating the build path
-----> Cloning the Git repository
-----> Installing gem dependencies using Bundler
-----> Moving to releases/4
-----> Symlinking to current
-----> Launching
-----> Done. Deployed v4
# Gemfile
gem "dotenv-rails"
# .env (.gitignore!)
SECRET_KEY=foo
# on heroku
heroku config:add SECRET_KEY=foo
# in your app
ENV["foo"]
Slide 38
Slide 38 text
No content
Slide 39
Slide 39 text
# Small App (5,761 records)
User Load (2.3ms) SELECT "users".* FROM "users" WHERE
"users"."username" = "jmccartie"
# Large App (231,138 records)
User Load (51.7ms) SELECT "users".* FROM "users" WHERE
"users"."username" = "jmccartie"
Slide 40
Slide 40 text
# Small App (5,761 records)
User Load (2.3ms) SELECT "users".* FROM "users" WHERE
"users"."username" = "jmccartie"
# Large App (231,138 records)
User Load (51.7ms) SELECT "users".* FROM "users" WHERE
"users"."username" = "jmccartie"
Slide 41
Slide 41 text
# Small App (5,761 records)
User Load (2.3ms) SELECT "users".* FROM "users" WHERE
"users"."username" = "jmccartie"
# Large App (231,138 records)
User Load (51.7ms) SELECT "users".* FROM "users" WHERE
"users"."username" = "jmccartie"
Slide 42
Slide 42 text
class AddIndexForUsernameOnUsers < ActiveRecord::Migration
def change
add_index :users, :username
end
end
Database Indexes
Slide 43
Slide 43 text
# Before
User Load (51.7ms) SELECT "users".* FROM "users" WHERE
"users"."username" = "jmccartie"
# After
User Load (1.0ms) SELECT "users".* FROM "users" WHERE
"users"."username" = "jmccartie"
Database Indexes
Slide 44
Slide 44 text
# Before
User Load (51.7ms) SELECT "users".* FROM "users" WHERE
"users"."username" = "jmccartie"
# After
User Load (1.0ms) SELECT "users".* FROM "users" WHERE
"users"."username" = "jmccartie"
Database Indexes
Slide 45
Slide 45 text
Intermission
Just two more chapters in our story…
Slide 46
Slide 46 text
No content
Slide 47
Slide 47 text
No content
Slide 48
Slide 48 text
Long-running requests consume resources
Off-load to background workers
Slide 49
Slide 49 text
# Problem
User.send_sms!(message)
# Solution
SendSmsJob.perform_later(@user, message)
Background Workers
Slide 50
Slide 50 text
class SendSmsJob < ActiveJob::Base
queue_as :default
def perform(user, message)
SmsProvider.send(user.phone, message)
end
end
Background Workers
Slide 51
Slide 51 text
So close…
I can feel that $1B valuation just around the corner…
Photo Load (0.2ms) SELECT "photos".* FROM "photos" ORDER BY name
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = 3
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = 5
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = 4
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = 6
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = 7
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = 8
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = 9
# Old
Photo Load (0.2ms) SELECT "photos".* FROM "photos" ORDER BY name
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = 3
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = 5
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = 4
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = 2
N+1 Queries
Slide 57
Slide 57 text
# New
Photo Load (0.2ms) SELECT "photos".* FROM "photos" LIMIT 0, 30
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id"
IN (3, 1, 5, 4, 2)
N+1 Queries