Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Facepalm To Foolproof - RailsConf 2016

Facepalm To Foolproof - RailsConf 2016

If you're new to Rails development, or just want some tips on deploying and running in production, this is the talk for you. Relying on real-world experience as part of the Heroku support team, we'll talk through common issues (and a few funny ones) we see when people take their "but it works in development!" app to a production environment.

Video: https://youtu.be/yDJV9mr--Yo

544e2e76286098a2c64c3638401d2191?s=128

Jon McCartie

May 04, 2016
Tweet

More Decks by Jon McCartie

Other Decks in Technology

Transcript

  1. Facepalm to Foolproof Avoiding Common Production Pitfalls Jon McCartie @jmccartie

  2. Hi there! ! I’m Jon McCartie. I work at Heroku.

    I help people fix things.
  3. None
  4. None
  5. None
  6. None
  7. None
  8. AirlessBNB Vacation Rentals In Outer Space

  9. None
  10. $ 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]
  11. $ rails server -e production => Booting WEBrick

  12. None
  13. # Gemfile gem 'puma' # Procfile web: bundle exec puma

    -C config/puma.rb
  14. $ 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]
  15. $ 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]
  16. ?

  17. None
  18. None
  19. 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?
  20. $ free -m total used free Mem: 8014 7528 486

    -/+ buffers/cache: 2446 5567 Swap: 511 7 504 Watch Your Memory Usage
  21. $ free -m total used free Mem: 8014 7528 486

    -/+ buffers/cache: 2446 5567 Swap: 511 7 504 Watch Your Memory Usage
  22. $ free -m total used free Mem: 8014 7528 486

    -/+ buffers/cache: 2446 5567 Swap: 511 7 504 Watch Your Memory Usage
  23. $ free -m total used free Mem: 8014 7528 486

    -/+ buffers/cache: 2446 5567 Swap: 511 7 504 Watch Your Memory Usage
  24. None
  25. http:/ /airlessbnb.com/checkout

  26. # config/production.rb config.force_ssl = true

  27. server { listen 80; return 301 https://$host$request_uri; } nginx.conf

  28. => ☄

  29. <image src="logo.png" /> 404 - Not Found

  30. # this /app/assets/images/logo.png # becomes this /public/assets/logo-a2c31b21374497ad4db1.png The Asset Pipeline

  31. # Nope <img src="logo.png" /> # Yup <%= image_tag "logo.png"

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

  33. # config/initializers/assets.rb Rails.application.config.assets.precompile += %w( ads.js ads.css) Precompile List

  34. None
  35. "Is this happening a lot?" Uhhh… maybe?

  36. "But Rails already logs to production.log" But we have multiple

    machines… How large is that single production.log file?
  37. 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/
  38. # config/environment.rb config.logger = Logger.new(STDOUT) Logging

  39. None
  40. $ ./bin/stop-production && \ sleep 4 && \ bundle exec

    ./bin/start-production
  41. None
  42. $ 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
  43. $ 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
  44. =>

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

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

    Credentials
  47. # Gemfile
 gem "dotenv-rails"
 # .env (.gitignore!)
 SECRET_KEY=foo # on

    heroku
 heroku config:add SECRET_KEY=foo Environment Variables
  48. # in your app
 ENV["foo"] # example REDIS = Redis.new(ENV["REDIS_URL"])

    Environment Variables
  49. None
  50. # 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"
  51. # 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"
  52. # 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"
  53. class AddIndexForUsernameOnUsers < ActiveRecord::Migration def change add_index :users, :username end

    end Database Indexes
  54. # 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"
  55. # 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"
  56. # Problem SELECT * FROM users 
 WHERE thing =

    'something' 
 AND other_thing = 'something_else' # Solution add_index :users, [:thing, :other_thing] Composite Indexes
  57. Intermission Just two more chapters in our story…

  58. So close… I can feel that $1B valuation just around

    the corner…
  59. # photos_controller.rb @photos = Photo.order(:name) # index.slim - @photos do

    |photo| = image_tag photo.url p= photo.user.username
  60. 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
  61. N+1

  62. # nope @photos = Photo.all # yup @photos = Photo.includes(:user)

    N+1 Queries
  63. # 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
  64. # 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
  65. # Gemfile gem 'bullet', group: :development N+1 Queries

  66. # 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
  67. None
  68. None
  69. None
  70. Request Controller Response S3

  71. SMS

  72. @user.send_sms!(message)

  73. Request Controller Response SMS

  74. Background Workers

  75. @user.send_sms!(message)

  76. SendSmsJob.perform_later(@user, message)

  77. Request Controller Response SMS

  78. Request Controller Response SMS

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

    message) end end
  80. None
  81. Running Your App In Production Keep learning

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

  83. Heroku Office Hours Tomorrow, 4:10pm Heroku Booth Heroku + Rails

    Core Team
  84. None