otimizando seu projeto rails

67
 Otimizando seu projeto Rails @timotta

Upload: tiago-albineli-motta

Post on 04-Jul-2015

753 views

Category:

Technology


1 download

DESCRIPTION

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

TRANSCRIPT

Page 1: Otimizando seu projeto Rails

   

Otimizando seu projeto Rails

@timotta

Page 2: Otimizando seu projeto Rails

   

Otimização prematura

Premature optimization is the root of all evil

-- DonaldKnuth

Page 3: Otimizando seu projeto Rails

   

Ruby é...

Lenta

Page 4: Otimizando seu projeto Rails

   

Rails é...

Lento

Page 5: Otimizando seu projeto Rails

   

O argumento...

Page 6: Otimizando seu projeto Rails

   

Porém...

EnfileiramentoDe requisições

Page 7: Otimizando seu projeto Rails

   

Page 8: Otimizando seu projeto Rails

   

Apache Benchmark

ab -n 1 -c 1 http://localhost:3000/politicos/piores

Page 9: Otimizando seu projeto Rails

   

Resultado de 1 requisição

ab -n 1 -c 1 http://localhost:3000/politicos/piores

Time taken for tests: 1.239 secondsRequests per second: 0.81 [#/sec]Time per request: 1239.403 [ms]

Page 10: Otimizando seu projeto Rails

   

Resultado de 400 por 20

ab -n 400 -c 20 http://localhost:3000/politicos/piores

Time taken for tests: 595.433 secondsRequests per second: 0.67 [#/sec]Time per request: 1488.583 [ms]

Page 11: Otimizando seu projeto Rails

   

Sempre a primeira supeita

Queries

Page 12: Otimizando seu projeto Rails

   

Verificando log

> rm log/production.log> GET http://localhost:3000/politicos/piores> grep -c select log/production.log216

Page 13: Otimizando seu projeto Rails

   

N+1 problem

Page 14: Otimizando seu projeto Rails

   

Usando include:

Politico.piores.include(:cargo,:partido)

Page 15: Otimizando seu projeto Rails

   

Queries

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

Page 16: Otimizando seu projeto Rails

   

N+1 problem

Page 17: Otimizando seu projeto Rails

   

Agrupando no controller

@query = Avaliacao. where(politico_id: @politicos, eleitor_id: @eleitor)

@avaliacoes = @query.reduce({}) do |grupo, avaliacao| grupo[avaliacao.politico_id] = avaliacao grupoend

Page 18: Otimizando seu projeto Rails

   

Usando na view

Antes:

@politico.avaliacoes.do_eleitor(@eleitor)

Agora:

@avaliacoes[@politico.id]

Page 19: Otimizando seu projeto Rails

   

Queries

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

Page 20: Otimizando seu projeto Rails

   

Apache Benchmark

ab -n 1 -c 1 http://localhost:3000/politicos/piores

Time 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/piores

Time taken for tests: 298.956 secondsRequests per second: 1.34 [#/sec]Time per request: 747.390 [ms]

Page 21: Otimizando seu projeto Rails

   

Evolução

Page 22: Otimizando seu projeto Rails

   

Vamos analisar mais...

Page 23: Otimizando seu projeto Rails

   

Cache dos filtros

def 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) %>

Page 24: Otimizando seu projeto Rails

   

Memcached

Gemfile:gem 'memcache-client'

environments:config.cache_store = :mem_cache_store,

['localhost:11211'], namespace: Rails.enva

Page 25: Otimizando seu projeto Rails

   

Resultado

ab -n 400 -c 20 http://localhost:3000/politicos/piores

Time taken for tests: 300.668 secondsRequests per second: 1.33 [#/sec]Time per request: 751.671 [ms]

Page 26: Otimizando seu projeto Rails

   

Evolução

Page 27: Otimizando seu projeto Rails

   

() sobre memcached

Rails 1

Rails 2

Rails 3

Memcache 1

Memcache 2

Memcache 3

Key ”ABC”

Key ”XYZ”

Key ”123”

Page 28: Otimizando seu projeto Rails

   

Vamos aproveitar os CORES

Mas e o GIL?http://goo.gl/CdsyZ

Page 29: Otimizando seu projeto Rails

   

Algumas opções

● Puma● Thin● Passenger● Unicorn

Page 30: Otimizando seu projeto Rails

   

Algumas opções

● Puma● Thin● Passenger● Unicorn

config.threadsafe!

Page 31: Otimizando seu projeto Rails

   

Unicorn

worker_processes 4preload_app truetimeout 30Listen 3000

after_fork do |server, worker| ActiveRecord::Base.establish_connectionend

Page 32: Otimizando seu projeto Rails

   

Resultado

ab -n 400 -c 20 http://localhost:3000/politicos/piores

Time taken for tests: 109.909 secondsRequests per second: 3.64 [#/sec]Time per request: 274.773 [ms]

Page 33: Otimizando seu projeto Rails

   

Evolução

Page 34: Otimizando seu projeto Rails

   

Indo mais fundo

Ferramentas paraencontrar gargalos

Page 35: Otimizando seu projeto Rails

   

newrelic

gem 'newrelic_rpm'

Page 36: Otimizando seu projeto Rails

   

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

Page 37: Otimizando seu projeto Rails

   

ruby-prof

Page 38: Otimizando seu projeto Rails

   

Ruby 1.9.3 e ruby-prof

http://goo.gl/u0hMd

Page 39: Otimizando seu projeto Rails

   

Patching Garbage Collector

https://github.com/skaes/rvm-patchsets

> rvm install 1.9.3-p125 --patch railsexpress --name railsexpress

> rvm use 1.9.3-p125-railsexpress@webdemocracia

Page 40: Otimizando seu projeto Rails

   

Patched Garbage Collector

GC.enable_statsENV['RAILS_ENV'] = 'production'4.times { get '/politicos/piores' }ENV['RAILS_ENV'] = 'test'

puts "allocated: #{GC.allocated_size/1024}K total” + "in #{GC.num_allocations} allocations, "puts "GC calls: #{GC.collections}, "puts "GC time: #{GC.time / 1000} msec"

Page 41: Otimizando seu projeto Rails

   

4 acessos geram...

allocated: 46409K total in 65930 allocations, GC calls: 6, GC time: 262 msec

Page 42: Otimizando seu projeto Rails

   

Gargalo nos ERBs

Page 43: Otimizando seu projeto Rails

   

Transformando ERBs em Str

def 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

Page 44: Otimizando seu projeto Rails

   

O que o GC nos diz

Antes:allocated: 46409K total in 65930 allocations, GC calls: 6, GC time: 262 msec

Depois:allocated: 45466K total in 63078 allocations, GC calls: 6, GC time: 261 msec

2852 a menos

Page 45: Otimizando seu projeto Rails

   

E o Apache Benchmark

ab -n 400 -c 20 http://localhost:3000/politicos/piores

Time taken for tests: 82.318 secondsRequests per second: 4.86 [#/sec]Time per request: 205.796 [ms]

Page 46: Otimizando seu projeto Rails

   

Evolução

Page 47: Otimizando seu projeto Rails

   

Ministério da segurança adverte

Trocar ERB por String podetornar seu código vulnerável

html e script injection

Page 48: Otimizando seu projeto Rails

   

Trocando bibliotecas

gem 'memcache-client'gem 'dalli'

Page 49: Otimizando seu projeto Rails

   

Benchmark

Benchmark.measure do 1000.times do |i| Rails.cache.write("a#{i}",i) Rails.cache.read("a#{i}") endend

Memcache-client: => 0.860000 0.020000 0.880000 ( 0.880819)

Dalli: => 0.270000 0.020000 0.290000 ( 0.302308)

Page 50: Otimizando seu projeto Rails

   

Resultado

ab -n 400 -c 20 http://localhost:3000/politicos/piores

Time taken for tests: 80.309 secondsRequests per second: 4.98 [#/sec]Time per request: 200.773 [ms]

Page 51: Otimizando seu projeto Rails

   

Evolução

Page 52: Otimizando seu projeto Rails

   

O que o ruby-prof nos diz...

41% do seu processamento

antes

Page 53: Otimizando seu projeto Rails

   

Rack Middlewares

> RAILS_ENV=production rake middlewareuse Rack::Cacheuse #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x0000000293e930>use Rack::Runtimeuse Rack::MethodOverrideuse ActionDispatch::RequestIduse Rails::Rack::Loggeruse ActionDispatch::ShowExceptionsuse ActionDispatch::DebugExceptionsuse ActionDispatch::RemoteIpuse Rack::Sendfileuse ActionDispatch::Callbacksuse ActiveRecord::ConnectionAdapters::ConnectionManagementuse ActiveRecord::QueryCacheuse ActionDispatch::Cookiesuse ActionDispatch::Session::CookieStoreuse ActionDispatch::Flashuse ActionDispatch::ParamsParseruse ActionDispatch::Headuse Rack::ConditionalGetuse Rack::ETaguse ActionDispatch::BestStandardsSupportuse ExceptionNotifieruse OmniAuth::Builderrun Webcracia::Application.routes

Page 54: Otimizando seu projeto Rails

   

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)

Page 55: Otimizando seu projeto Rails

   

Efeitos colaterais...

404 antes 404 depois

Page 56: Otimizando seu projeto Rails

   

O que o GC nos diz...

Antes:allocated: 45466K total in 63078 allocations, GC calls: 6, GC time: 261 msec

Antes:allocated: 45329K total in 61967 allocations, GC calls: 6, GC time: 250 msec

1111 a menos

Page 57: Otimizando seu projeto Rails

   

E o Apache Benchmark

ab -n 400 -c 20 http://localhost:3000/politicos/piores

Time taken for tests: 75.588 secondsRequests per second: 5.29 [#/sec]Time per request: 188.970 [ms]

Page 58: Otimizando seu projeto Rails

   

Evolução

Page 59: Otimizando seu projeto Rails

   

Mais ruby-prof

15% paraGerar os filtros

Da lateral

Page 60: Otimizando seu projeto Rails

   

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 endend

Page 61: Otimizando seu projeto Rails

   

Resultado

ab -n 400 -c 20 http://localhost:3000/politicos/piores

Time taken for tests: 73.719 secondsRequests per second: 5.43 [#/sec]Time per request: 184.296 [ms]

Page 62: Otimizando seu projeto Rails

   

Evolução

Page 63: Otimizando seu projeto Rails

   

Requests por segundo

Page 64: Otimizando seu projeto Rails

   

Ferramentas

● Apache Benchmark● Newrelic● Ruby-prof● Benckmark.mesure● Patched GC

Page 65: Otimizando seu projeto Rails

   

Problemas/soluções comuns

● N+1 problem● Cache● Paralelização● ERB● Middlewares

Page 66: Otimizando seu projeto Rails

   

Outras técnicas

● Separar site cacheávelhttp://goo.gl/SyTnB

● Utilizando o Nginx para escalarhttp://goo.gl/k9H7D

Page 67: Otimizando seu projeto Rails

   

Entre em contato

@[email protected]://programandosemcafeina.blogspot.com