SlideShare a Scribd company logo
Optimizing rails applications’
                                 performance
                               with Asset CDN and Unicorn

                                            Simon Bagreev, @status_200
                                                    sbagreev@gmail.com




Friday, January 11, 13
Question
                         Does anyone know what

                                 f5dd
                                  is?




Friday, January 11, 13
What This Preso is NOT

                         • not a coding demo or tutorial (strangely)
                         • not a best practices showcase




Friday, January 11, 13
What This Preso IS

                         • tips and tricks on tuning rails application
                         • personal experience




Friday, January 11, 13
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
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
Sample App




Friday, January 11, 13
NewRelic Monitoring




                         average load time for mobile traffic only, includes iframed ads




Friday, January 11, 13
Heroku Dyno Operation




Friday, January 11, 13
Step 1 - Rack::Cache




                  Serving assets from rack::cache is faster, and frees up app instance to serve more requests




Friday, January 11, 13
Static Asset Caching
                         # Gemfile
                         gem 'dalli'

                         # config/application.rb
                         config.cache_store = :dalli_store

                         # config/environments/production.rb
                         config.action_dispatch.rack_cache = {
                           :metastore    => Dalli::Client.new,
                           :entitystore => 'file:tmp/cache/rack/body',
                           :allow_reload => false
                         }
                         config.serve_static_assets = true
                         config.assets.digest = true

                         config.action_controller.perform_caching = true
                         # provision Memcache addon on Heroku




Friday, January 11, 13
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
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
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
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
Now, on git push heroku




                         assets are automatically synced to s3 anytime ON rake assets:precompile




Friday, January 11, 13
S3 Bucket effect




                         down from 4.45 / 179 / 2.43 before the change -- even better!




Friday, January 11, 13
Step 3 - AWS CloudFront




Friday, January 11, 13
CloudFront Effect




                         down from 3.85 / 179 / 2.19 before the change -- Awesome!




Friday, January 11, 13
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
WebPageTest ResultsBefore




Friday, January 11, 13
WebPageTest Results After




Friday, January 11, 13
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
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
Unicorn for Rails App
                         # Gemfile
                         gem 'unicorn'

                         # Procfile
                         web: bundle exec unicorn -p $PORT -c ./config/unicorn.rb

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

                         # also, add config/unicorn.rb




Friday, January 11, 13
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
After Implementing Unicorn




                         down from 3.64 / 46.7 / 1.17 before the change -- good, but also...




Friday, January 11, 13
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
And ...




                         my app can process six concurrent requests on two heroku dynos




Friday, January 11, 13
Riding Unicorn




Friday, January 11, 13
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
Friday, January 11, 13
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
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

More Related Content

PDF
How we use and deploy Varnish at Opera
PDF
Automated Java Deployments With Rpm
PDF
TorqueBox at DC:JBUG - November 2011
PDF
Puppet at Opera Sofware - PuppetCamp Oslo 2013
PDF
Tunning mobicent-jean deruelle
PDF
Build Automation 101
PDF
Hong Qiangning in QConBeijing
KEY
Devignition 2011
How we use and deploy Varnish at Opera
Automated Java Deployments With Rpm
TorqueBox at DC:JBUG - November 2011
Puppet at Opera Sofware - PuppetCamp Oslo 2013
Tunning mobicent-jean deruelle
Build Automation 101
Hong Qiangning in QConBeijing
Devignition 2011

What's hot (20)

KEY
Making Your Capistrano Recipe Book
PPTX
IT Infrastructure Through The Public Network Challenges And Solutions
KEY
Puppet for dummies - ZendCon 2011 Edition
PDF
Islands: Puppet at Bulletproof Networks
PDF
Scaling Mapufacture on Amazon Web Services
PDF
Fixing Growing Pains With Puppet Data Patterns
PDF
Performance Improvements in Browsers
PDF
Ansible 實戰:top down 觀點
PDF
TorqueBox - When Java meets Ruby
PDF
Getting started with Ansible
PDF
JUDCon 2010 Boston : BoxGrinder
PDF
Complex Made Simple: Sleep Better with TorqueBox
PDF
TorqueBox: The beauty of Ruby with the power of JBoss. Presented at Devnexus...
PDF
Practicing Continuous Deployment
PDF
Challenges when building high profile editorial sites
PDF
Practical Performance: Understand and improve the performance of your applica...
PDF
kubernetes practice
PPT
Scalable Systems Management with Puppet
PDF
Taming the Cloud Database with Apache jclouds, ApacheCon Europe 2014
PDF
Docker Online Meetup #3: Docker in Production
Making Your Capistrano Recipe Book
IT Infrastructure Through The Public Network Challenges And Solutions
Puppet for dummies - ZendCon 2011 Edition
Islands: Puppet at Bulletproof Networks
Scaling Mapufacture on Amazon Web Services
Fixing Growing Pains With Puppet Data Patterns
Performance Improvements in Browsers
Ansible 實戰:top down 觀點
TorqueBox - When Java meets Ruby
Getting started with Ansible
JUDCon 2010 Boston : BoxGrinder
Complex Made Simple: Sleep Better with TorqueBox
TorqueBox: The beauty of Ruby with the power of JBoss. Presented at Devnexus...
Practicing Continuous Deployment
Challenges when building high profile editorial sites
Practical Performance: Understand and improve the performance of your applica...
kubernetes practice
Scalable Systems Management with Puppet
Taming the Cloud Database with Apache jclouds, ApacheCon Europe 2014
Docker Online Meetup #3: Docker in Production
Ad

Viewers also liked (6)

PDF
Slides with notes from Ruby Conf 2014 on using simple techniques to create sl...
PDF
React on rails v4
PDF
Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, H...
ODP
xUnit and TDD: Why and How in Enterprise Software, August 2012
PDF
React on rails v6.1 at LA Ruby, November 2016
PPTX
The Art of Product Marketing
Slides with notes from Ruby Conf 2014 on using simple techniques to create sl...
React on rails v4
Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, H...
xUnit and TDD: Why and How in Enterprise Software, August 2012
React on rails v6.1 at LA Ruby, November 2016
The Art of Product Marketing
Ad

Similar to Improving Your Heroku App Performance with Asset CDN and Unicorn (20)

PDF
Aloha on-rails-2009
PDF
Cloudcamp Athens 2011 Presenting Heroku
PDF
Introduction to node.js by Ran Mizrahi @ Reversim Summit
PDF
One Page, One App -or- How to Write a Crawlable Single Page Web App
PDF
Rails Application Optimization Techniques & Tools
PPTX
Lean microservices through ahead of time compilation (Tobias Piper, Loveholid...
PDF
Red Dirt Ruby Conference
PDF
Puppet and AWS: Getting the best of both worlds
PDF
Big Data & Cloud | Cloud Storage Simplified | Adrian Cole
PDF
Embulk, an open-source plugin-based parallel bulk data loader
PDF
Unleashing the Rails Asset Pipeline
PDF
Rubypalooza 2009
PDF
Distributed Queue System using Gearman
PPTX
Alfresco Environment Validation and "Day Zero" Configuration
PPTX
Deployment with Fabric
PDF
Think_your_Postgres_backups_and_recovery_are_safe_lets_talk.pptx
PDF
RubyConf 2009
PPT
Apache Traffic Server
PDF
Writing Plugged-in Java EE Apps: Jason Lee
PDF
Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache
Aloha on-rails-2009
Cloudcamp Athens 2011 Presenting Heroku
Introduction to node.js by Ran Mizrahi @ Reversim Summit
One Page, One App -or- How to Write a Crawlable Single Page Web App
Rails Application Optimization Techniques & Tools
Lean microservices through ahead of time compilation (Tobias Piper, Loveholid...
Red Dirt Ruby Conference
Puppet and AWS: Getting the best of both worlds
Big Data & Cloud | Cloud Storage Simplified | Adrian Cole
Embulk, an open-source plugin-based parallel bulk data loader
Unleashing the Rails Asset Pipeline
Rubypalooza 2009
Distributed Queue System using Gearman
Alfresco Environment Validation and "Day Zero" Configuration
Deployment with Fabric
Think_your_Postgres_backups_and_recovery_are_safe_lets_talk.pptx
RubyConf 2009
Apache Traffic Server
Writing Plugged-in Java EE Apps: Jason Lee
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
  • 8. NewRelic Monitoring average load time for mobile traffic only, includes iframed ads Friday, January 11, 13
  • 10. Step 1 - Rack::Cache Serving assets from rack::cache is faster, and frees up app instance to serve more requests Friday, January 11, 13
  • 11. Static Asset Caching # Gemfile gem 'dalli' # config/application.rb config.cache_store = :dalli_store # config/environments/production.rb config.action_dispatch.rack_cache = { :metastore => Dalli::Client.new, :entitystore => 'file:tmp/cache/rack/body', :allow_reload => false } config.serve_static_assets = true config.assets.digest = true config.action_controller.perform_caching = true # provision Memcache addon on Heroku 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
  • 25. Unicorn for Rails App # Gemfile gem 'unicorn' # Procfile web: bundle exec unicorn -p $PORT -c ./config/unicorn.rb # config/application.rb config.logger = Logger.new(STDOUT) # also, add config/unicorn.rb 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