Slide 1

Slide 1 text

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]

Slide 11

Slide 11 text

$ rails server -e production => Booting WEBrick

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

# Gemfile gem ‘puma' # Procfile web: bundle exec puma -t 5:5 -p ${PORT:-3000} -e ${RACK_ENV:-development}

Slide 14

Slide 14 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]

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

Slide 28

Slide 28 text

# Nope .logo background-image: url("/assets/logo.png") # Yup .logo background-image: image-url("logo.png") Your CSS

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

$ ./bin/stop-production && \ sleep 4 && \ bundle exec ./bin/start-production

Slide 31

Slide 31 text

No content

Slide 32

Slide 32 text

$ 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

Slide 33

Slide 33 text

$ heroku create sushi Creating sushi... done http://sushi.herokuapp.com/ | git@heroku.com:sushi.git $ git push heroku master ----> Heroku receiving push ----> Rails app detected ----> Compiled slug size is 8.0MB http://sushi.herokuapp.com deployed to heroku

Slide 34

Slide 34 text

=>

Slide 35

Slide 35 text

$ cat config/s3.yml production: bucket: airlessbnb_production access_key_id: d6bb03e1e761dbaebb881e7a8c7a8fcc secret_access_key: 8921377d4ab6da922f5d8a12fd993ca9

Slide 36

Slide 36 text

# config/secrets.yml (.gitignore!) development: some_api_key: SOMEKEY # Usage Rails.application.secrets.some_api_key Storing Credentials

Slide 37

Slide 37 text

# 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…

Slide 52

Slide 52 text

# photos_controller.rb @photos = Photo.order(:name) # index.slim - @photos do |photo| = image_tag photo.url p= photo.user.username

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

N+1

Slide 55

Slide 55 text

# nope @photos = Photo.all # yup @photos = Photo.includes(:user) N+1 Queries

Slide 56

Slide 56 text

# 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

Slide 58

Slide 58 text

# Gemfile gem 'bullet', group: :development # Logs 2016-02-25 20:40:17[INFO] N+1 Query: PATH_INFO: /photos; model: Photo => associations: [users]· Add to your finder: :include => [:users] N+1 Queries

Slide 59

Slide 59 text

No content

Slide 60

Slide 60 text

No content

Slide 61

Slide 61 text

No content

Slide 62

Slide 62 text

Running Your App In Production Keep learning

Slide 63

Slide 63 text

Thank you! Jon McCartie @jmccartie Slides: http:/ /bit.ly/rci2016