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 app consume in one web worker?
- how much memory do you have?
Slide 20
Slide 20 text
$ free -m
total used free
Mem: 8014 7528 486
-/+ buffers/cache: 2446 5567
Swap: 511 7 504
Watch Your Memory Usage
Slide 21
Slide 21 text
$ free -m
total used free
Mem: 8014 7528 486
-/+ buffers/cache: 2446 5567
Swap: 511 7 504
Watch Your Memory Usage
Slide 22
Slide 22 text
$ free -m
total used free
Mem: 8014 7528 486
-/+ buffers/cache: 2446 5567
Swap: 511 7 504
Watch Your Memory Usage
Slide 23
Slide 23 text
$ free -m
total used free
Mem: 8014 7528 486
-/+ buffers/cache: 2446 5567
Swap: 511 7 504
Watch Your Memory Usage
Slide 24
Slide 24 text
➡
Slide 25
Slide 25 text
No content
Slide 26
Slide 26 text
http:/
/airlessbnb.com/checkout
Slide 27
Slide 27 text
# config/production.rb
config.force_ssl = true
Slide 28
Slide 28 text
server {
listen 80;
return 301 https://$host$request_uri;
}
nginx.conf
Slide 29
Slide 29 text
=> ☄
Slide 30
Slide 30 text
404 - Not Found
Slide 31
Slide 31 text
# this
/app/assets/images/logo.png
# becomes this
/public/assets/logo-a2c31b21374497ad4db1.png
The Asset Pipeline
# config/initializers/assets.rb
Rails.application.config.assets.precompile +=
%w( ads.js ads.css)
Precompile List
Slide 35
Slide 35 text
No content
Slide 36
Slide 36 text
"Is this happening a lot?"
Uhhh… maybe?
Slide 37
Slide 37 text
"But Rails already logs to production.log"
But we have multiple machines…
How large is that single production.log file?
Slide 38
Slide 38 text
Logging
A twelve-factor app never concerns itself with routing or
storage of its output stream. It should not attempt to write
to or manage logfiles. Instead, each running process writes its
event stream, unbuffered, to stdout.
http:/
/12factor.net/
$ 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
# in your app
ENV["foo"]
# example
REDIS = Redis.new(ENV["REDIS_URL"])
Environment Variables
Slide 50
Slide 50 text
No content
Slide 51
Slide 51 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 52
Slide 52 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 53
Slide 53 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 54
Slide 54 text
class AddIndexForUsernameOnUsers < ActiveRecord::Migration
def change
add_index :users, :username
end
end
Database Indexes
Slide 55
Slide 55 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"
Slide 56
Slide 56 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"
Slide 57
Slide 57 text
# Problem
SELECT * FROM users
WHERE thing = 'something'
AND other_thing = 'something_else'
# Solution
add_index :users, [:thing, :other_thing]
Composite Indexes
Slide 58
Slide 58 text
Intermission
Just two more chapters in our story…
Slide 59
Slide 59 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 65
Slide 65 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