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 -C config/puma.rb

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

Slide 32

Slide 32 text

# Nope # Yup <%= image_tag "logo.png" %>

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 39

Slide 39 text

# config/environment.rb config.logger = Logger.new(STDOUT) Logging

Slide 40

Slide 40 text

No content

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

No content

Slide 43

Slide 43 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 44

Slide 44 text

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

Slide 45

Slide 45 text

=>

Slide 46

Slide 46 text

$ cat config/s3.yml production: bucket: airlessbnb_production access_key_id: d6bb03e1e761d secret_access_key: 8921377d4ab6da9

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

# Gemfile
 gem "dotenv-rails"
 # .env (.gitignore!)
 SECRET_KEY=foo # on heroku
 heroku config:add SECRET_KEY=foo Environment Variables

Slide 49

Slide 49 text

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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 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 62

Slide 62 text

N+1

Slide 63

Slide 63 text

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

Slide 64

Slide 64 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 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

Slide 66

Slide 66 text

# Gemfile gem 'bullet', group: :development N+1 Queries

Slide 67

Slide 67 text

# Logs 2016-04-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 68

Slide 68 text

No content

Slide 69

Slide 69 text

No content

Slide 70

Slide 70 text

No content

Slide 71

Slide 71 text

Request Controller Response S3

Slide 72

Slide 72 text

SMS

Slide 73

Slide 73 text

@user.send_sms!(message)

Slide 74

Slide 74 text

Request Controller Response SMS

Slide 75

Slide 75 text

Background Workers

Slide 76

Slide 76 text

@user.send_sms!(message)

Slide 77

Slide 77 text

SendSmsJob.perform_later(@user, message)

Slide 78

Slide 78 text

Request Controller Response SMS

Slide 79

Slide 79 text

Request Controller Response SMS

Slide 80

Slide 80 text

class SendSmsJob < ActiveJob::Base queue_as :default def perform(user, message) SmsProvider.send(user.phone, message) end end

Slide 81

Slide 81 text

No content

Slide 82

Slide 82 text

Slide 83

Slide 83 text

Running Your App In Production Keep learning

Slide 84

Slide 84 text

Thank you! Jon McCartie @jmccartie bit.ly/railsconf16

Slide 85

Slide 85 text

Heroku Office Hours Tomorrow, 4:10pm Heroku Booth Heroku + Rails Core Team

Slide 86

Slide 86 text

No content