Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache
Improving Your Heroku App Performance with Asset CDN and Unicorn
1. Optimizing rails applications’
performance
with Asset CDN and Unicorn
Simon Bagreev, @status_200
sbagreev@gmail.com
Friday, January 11, 13
2. Question
Does anyone know what
f5dd
is?
Friday, January 11, 13
3. What This Preso is NOT
• not a coding demo or tutorial (strangely)
• not a best practices showcase
Friday, January 11, 13
4. What This Preso IS
• tips and tricks on tuning rails application
• personal experience
Friday, January 11, 13
5. Disclaimer
There are many other ways to improve app’s performance:
•database performance (indexes, N+1, slow queries)
•caching
•background processing
•changing interpreter, GC
•conditional asset loading, etc
•removing cruft!
Try them first!
Friday, January 11, 13
6. This Presentation - Two Parts
• CDN Asset Host using aws*
• unicorn web server*
* Both approaches were tested on heroku, but must work with other hosting solutions
Friday, January 11, 13
12. After Deployment
should see entries like this in your log
cache: [GET /assets/application-c0747cab950350f59304a3815f980622.css] miss, store
cache: [GET /assets/application-032691d4988a7003c42a10995819a0ce.js] miss, store
cache: [GET /assets/s_code.js] miss, store
cache: [GET /assets/application-c0747cab950350f59304a3815f980622.css] fresh
cache: [GET /assets/application-032691d4988a7003c42a10995819a0ce.js] fresh
cache: [GET /assets/s_code.js] fresh
Friday, January 11, 13
13. Using Rack::Cache Effect
down from 4.91 / 201 / 2.29 before the change -- not bad for 8 lines of code!
Friday, January 11, 13
14. Step 2 - S3 bucket for assets
heroku instance has more time to serve application code because all assets are served from aws s3
Friday, January 11, 13
15. S3 Bucket for Assets
# Gemfile
gem "asset_sync" # will push compiled assets into CDN
# Command line
heroku config:add FOG_PROVIDER=AWS
AWS_ACCESS_KEY_ID=xxx
AWS_SECRET_ACCESS_KEY=yyy
heroku config:add FOG_DIRECTORY=yourappname-assets
# config/environments/production.rb
config.action_controller.asset_host =
"//#{ENV['FOG_DIRECTORY']}.s3.amazonaws.com"
# make sure to use AssetTagHelper methods (like image_tag)
# to ensure assets are properly referenced
Friday, January 11, 13
16. Now, on git push heroku
assets are automatically synced to s3 anytime ON rake assets:precompile
Friday, January 11, 13
17. S3 Bucket effect
down from 4.45 / 179 / 2.43 before the change -- even better!
Friday, January 11, 13
18. Step 3 - AWS CloudFront
Friday, January 11, 13
19. CloudFront Effect
down from 3.85 / 179 / 2.19 before the change -- Awesome!
Friday, January 11, 13
20. Loading Single File
$ time curl http://careersingear.mobi/assets/application-
bdb77a926724ccc3c20b923ab168d89d.js
real 0m0.896s
user 0m0.008s
sys 0m0.016s
----------------
$ time curl http://d3kd72psxbec02.cloudfront.net/assets/
application-bdb77a926724ccc3c20b923ab168d89d.js
real 0m0.293s
user 0m0.006s
sys 0m0.010s
getting a single application.js file from cloud front is 3x faster
Friday, January 11, 13
23. Meet Unicorn
• HTTp server for Ruby
• Starts one master process
• forks worker processes
• workers handle requests
• master returns
• one port, several
concurrent requests
Friday, January 11, 13
24. Server Setup
Unicorn setup
classic setup
nginx -> unix domain socket -> unicorn
nginx -> smart balancer -> pool of mongrels
workers (os handles load balancing)
Friday, January 11, 13
26. Unicorn for Rails App
# config/unicorn.rb
worker_processes 3
timeout 30
preload_app true
before_fork do |server, worker|
if defined?(ActiveRecord::Base)
ActiveRecord::Base.connection.disconnect!
Rails.logger.info('Disconnected from ActiveRecord')
end
if defined?(Resque)
Resque.redis.quit
Rails.logger.info('Disconnected from Redis')
end
end
after_fork do |server, worker|
if defined?(ActiveRecord::Base)
ActiveRecord::Base.establish_connection
Rails.logger.info('Connected to ActiveRecord')
end
if defined?(Resque)
Resque.redis = ENV["REDISTOGO_URL"]
Rails.logger.info('Connected to Redis')
end
end
Friday, January 11, 13
27. After Implementing Unicorn
down from 3.64 / 46.7 / 1.17 before the change -- good, but also...
Friday, January 11, 13
28. Better Concurrency Handling
require 'typhoeus'
require "benchmark"
URL = "http://careersingear.mobi"
HYDRA = Typhoeus::Hydra.new(max_concurrency: 20)
1000.times do
request = Typhoeus::Request.new(URL, method: :get, timeout: 10000)
request.on_complete do |response|
puts response.code
end
HYDRA.queue(request)
end
Benchmark.bm(7) do |x|
x.report("first:") { HYDRA.run }
end
# using thin
# user system total real
# 1.030000 0.380000 1.410000 ( 16.713791)
# using unicorn
# user system total real
# 1.050000 0.390000 1.440000 ( 7.843766)
Friday, January 11, 13
29. And ...
my app can process six concurrent requests on two heroku dynos
Friday, January 11, 13
31. To Conclude
• implemented asset cdn
• configured unicorn
• brought down average end user load time from almost
5 sec to 3.5 sec
• app can serve more requests faster and for less $$$
Friday, January 11, 13
33. Credits
defunkt, unicorn! https://github.com/blog/517-unicorn
heroku dev center, using rack::cache with memcached in rails 3.1+ https://devcenter.heroku.com/articles/
rack-cache-memcached-rails31
Rice, david, using a cdn asset host with rails 3.1 https://devcenter.heroku.com/articles/cdn-asset-
host-rails31
Sikkes, Michael, Complete Guide to serving your Rails assets over S3 with asset_sync http://blog.firmhouse.com/
complete-guide-to-serving-your-rails-assets-over-s3-with-asset_sync
van roijen, michael, more concurrency on a single heroku dyno with the new celadon cedar stack http://
michaelvanrooijen.com/articles/2011/06/01-more-concurrency-on-a-single-heroku-dyno-
with-the-new-celadon-cedar-stack/
Friday, January 11, 13
34. Q&A
This presentation can be found on github github.com/semmin/asset-cdn-and-unicorn-preso
twitter: @status_200
Email: sbagreev@gmail.com
Questions?
Friday, January 11, 13