Otimizando seu projeto Rails
Upcoming SlideShare
Loading in...5
×
 

Otimizando seu projeto Rails

on

  • 927 views

Slides da palestra que apresentei no Rock and Rails de Vila Velha em outubro de 2012

Slides da palestra que apresentei no Rock and Rails de Vila Velha em outubro de 2012

Statistics

Views

Total Views
927
Views on SlideShare
925
Embed Views
2

Actions

Likes
5
Downloads
7
Comments
4

1 Embed 2

https://si0.twimg.com 2

Accessibility

Categories

Upload Details

Uploaded via as OpenOffice

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Otimizando seu projeto Rails Otimizando seu projeto Rails Presentation Transcript

  • Otimizando seu projeto Rails @timotta   
  • Otimização prematura Premature optimization is the root of all evil -- DonaldKnuth   
  • Ruby é... Lenta   
  • Rails é... Lento   
  • O argumento...   
  • Porém... Enfileiramento De requisições   
  •    
  • Apache Benchmark ab -n 1 -c 1 http://localhost:3000/politicos/piores   
  • Resultado de 1 requisiçãoab -n 1 -c 1 http://localhost:3000/politicos/pioresTime taken for tests: 1.239 secondsRequests per second: 0.81 [#/sec]Time per request: 1239.403 [ms]   
  • Resultado de 400 por 20ab -n 400 -c 20 http://localhost:3000/politicos/pioresTime taken for tests: 595.433 secondsRequests per second: 0.67 [#/sec]Time per request: 1488.583 [ms]   
  • Sempre a primeira supeita Queries   
  • Verificando log > rm log/production.log > GET http://localhost:3000/politicos/piores > grep -c select log/production.log 216   
  • N+1 problem   
  • Usando include: Politico.piores.include(:cargo,:partido)   
  • QueriesSELECT `cargos`.* FROM `cargos`WHERE `cargos`.`id` IN (4, 5, 1, 2, 8, 6, 3, 9)SELECT `partidos`.* FROM `partidos`WHERE `partidos`.`id` IN (3, 9, 12, 11, 6, 4, 1)> rm log/production.log> GET http://localhost:3000/politicos/piores> grep -c Load log/production.log191   
  • N+1 problem   
  • Agrupando no controller@query = Avaliacao. where(politico_id: @politicos, eleitor_id: @eleitor)@avaliacoes = @query.reduce({}) do |grupo, avaliacao| grupo[avaliacao.politico_id] = avaliacao grupoend   
  • Usando na view Antes: @politico.avaliacoes.do_eleitor(@eleitor) Agora: @avaliacoes[@politico.id]   
  • QueriesSELECT `avaliacoes`.* FROM `avaliacoes`WHERE `avaliacoes`.`politico_id` IN (640, 620, 639, 683, ...)AND `avaliacoes`.`eleitor_id` = 6275> rm log/production.log> GET http://localhost:3000/politicos/piores> grep -c select log/production.log7   
  • Apache Benchmarkab -n 1 -c 1 http://localhost:3000/politicos/pioresTime taken for tests: 0.658 secondsRequests per second: 1.52 [#/sec]Time per request: 658.123 [ms]ab -n 400 -c 20 http://localhost:3000/politicos/pioresTime taken for tests: 298.956 secondsRequests per second: 1.34 [#/sec]Time per request: 747.390 [ms]   
  • Evolução   
  • Vamos analisar mais...   
  • Cache dos filtrosdef cargos_for_select Rails.cache.fetch(cargos_for_select) do Cargo.all.collect do |cargo| [ cargo.no_plural, cargo.slug ] end endend<%= options_for_select(cargos_for_select) %>   
  • Memcached Gemfile: gem memcache-client environments: config.cache_store = :mem_cache_store, [localhost:11211], namespace: Rails.env a   
  • Resultadoab -n 400 -c 20 http://localhost:3000/politicos/pioresTime taken for tests: 300.668 secondsRequests per second: 1.33 [#/sec]Time per request: 751.671 [ms]   
  • Evolução   
  • () sobre memcached Rails 1 Memcache 1 Key ”ABC” Rails 2 Memcache 2 Key ”XYZ” Rails 3 Memcache 3 Key ”123”   
  • Vamos aproveitar os CORES Mas e o GIL? http://goo.gl/CdsyZ   
  • Algumas opções ● Puma ● Thin ● Passenger ● Unicorn   
  • Algumas opções ● Puma config.threadsafe! ● Thin ● Passenger ● Unicorn   
  • Unicorn worker_processes 4 preload_app true timeout 30 Listen 3000 after_fork do |server, worker| ActiveRecord::Base.establish_connection end   
  • Resultadoab -n 400 -c 20 http://localhost:3000/politicos/pioresTime taken for tests: 109.909 secondsRequests per second: 3.64 [#/sec]Time per request: 274.773 [ms]   
  • Evolução   
  • Indo mais fundo Ferramentas para encontrar gargalos   
  • newrelicgem newrelic_rpm   
  • ruby-prof gem ruby-prof > rails generate performance_test politicos > rake test:profile def test_piores ENV[RAILS_ENV] = production get /politicos/piores ENV[RAILS_ENV] = test end   
  • ruby-prof   
  • Ruby 1.9.3 e ruby-prof http://goo.gl/u0hMd   
  • Patching Garbage Collectorhttps://github.com/skaes/rvm-patchsets> rvm install 1.9.3-p125 --patch railsexpress --name railsexpress> rvm use 1.9.3-p125-railsexpress@webdemocracia   
  • Patched Garbage CollectorGC.enable_statsENV[RAILS_ENV] = production4.times { get /politicos/piores }ENV[RAILS_ENV] = testputs "allocated: #{GC.allocated_size/1024}K total” + "in #{GC.num_allocations} allocations, "puts "GC calls: #{GC.collections}, "puts "GC time: #{GC.time / 1000} msec"   
  • 4 acessos geram...allocated: 46409K total in 65930 allocations,GC calls: 6,GC time: 262 msec   
  • Gargalo nos ERBs   
  • Transformando ERBs em Strdef botoes_para_avaliar(politico, avaliacao) html = <<-RETORNO <div class="avaliar"> #{texto_de_avaliacao avaliacao}:<br/> #{html_de_avaliacao_negativa politico, avaliacao} #{html_de_avaliacao_positiva politico, avaliacao} </div> RETORNO html.html_safeend    
  • O que o GC nos dizAntes:allocated: 46409K total in 65930 allocations,GC calls: 6,GC time: 262 msec 2852 a menosDepois:allocated: 45466K total in 63078 allocations,GC calls: 6,GC time: 261 msec   
  • E o Apache Benchmarkab -n 400 -c 20 http://localhost:3000/politicos/pioresTime taken for tests: 82.318 secondsRequests per second: 4.86 [#/sec]Time per request: 205.796 [ms]   
  • Evolução   
  • Ministério da segurança adverte Trocar ERB por String pode tornar seu código vulnerável html e script injection   
  • Trocando bibliotecas gem memcache-client gem dalli   
  • Benchmark Benchmark.measure do 1000.times do |i| Rails.cache.write("a#{i}",i) Rails.cache.read("a#{i}") end end Memcache-client: => 0.860000 0.020000 0.880000 ( 0.880819) Dalli: => 0.270000 0.020000 0.290000 ( 0.302308)   
  • Resultadoab -n 400 -c 20 http://localhost:3000/politicos/pioresTime taken for tests: 80.309 secondsRequests per second: 4.98 [#/sec]Time per request: 200.773 [ms]   
  • Evolução   
  • O que o ruby-prof nos diz... 41% do seu processamento antes   
  • Rack Middlewares > RAILS_ENV=production rake middleware use Rack::Cache use #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x0000000293e930> use Rack::Runtime use Rack::MethodOverride use ActionDispatch::RequestId use Rails::Rack::Logger use ActionDispatch::ShowExceptions use ActionDispatch::DebugExceptions use ActionDispatch::RemoteIp use Rack::Sendfile use ActionDispatch::Callbacks use ActiveRecord::ConnectionAdapters::ConnectionManagement use ActiveRecord::QueryCache use ActionDispatch::Cookies use ActionDispatch::Session::CookieStore use ActionDispatch::Flash use ActionDispatch::ParamsParser use ActionDispatch::Head use Rack::ConditionalGet use Rack::ETag use ActionDispatch::BestStandardsSupport use ExceptionNotifier use OmniAuth::Builder run Webcracia::Application.routes   
  • Removendo alguns...config.middleware.delete(ActionDispatch::RemoteIp)config.middleware.delete(Rack::Sendfile)config.middleware.delete(ActionDispatch::RequestId)config.middleware.delete(Rack::Cache)config.middleware.delete(ActionDispatch::Callbacks)config.middleware.delete(Rack::ConditionalGet)config.middleware.delete(Rack::ETag)config.middleware.delete(ActionDispatch::BestStandardsSupport)config.middleware.delete(ActiveSupport::Cache::Strategy::LocalCache)config.middleware.delete(ActionDispatch::DebugExceptions)config.middleware.delete(ActionDispatch::ShowExceptions)config.middleware.delete(ActionDispatch::Head)    
  • Efeitos colaterais... 404 antes 404 depois   
  • O que o GC nos diz... Antes: allocated: 45466K total in 63078 allocations, GC calls: 6, GC time: 261 msec 1111 a menos Antes: allocated: 45329K total in 61967 allocations, GC calls: 6, GC time: 250 msec   
  • E o Apache Benchmarkab -n 400 -c 20 http://localhost:3000/politicos/pioresTime taken for tests: 75.588 secondsRequests per second: 5.29 [#/sec]Time per request: 188.970 [ms]   
  • Evolução   
  • Mais ruby-prof 15% para Gerar os filtros Da lateral   
  • Mais cache def html_de_filtros Rails.cache.fetch(filtros) do html = <<-RETORNO <div class="filtros">Filtrar por:<br/> #{select_de_cargos} #{select_de_partidos} #{select_de_estados} </div>" RETORNO html.html_safe end  end  
  • Resultadoab -n 400 -c 20 http://localhost:3000/politicos/pioresTime taken for tests: 73.719 secondsRequests per second: 5.43 [#/sec]Time per request: 184.296 [ms]   
  • Evolução   
  • Requests por segundo   
  • Ferramentas ● Apache Benchmark ● Newrelic ● Ruby-prof ● Benckmark.mesure ● Patched GC   
  • Problemas/soluções comuns ● N+1 problem ● Cache ● Paralelização ● ERB ● Middlewares   
  • Outras técnicas ● Separar site cacheável http://goo.gl/SyTnB ● Utilizando o Nginx para escalar http://goo.gl/k9H7D   
  • Entre em contato @timotta timotta@gmail.com http://programandosemcafeina.blogspot.com