Otimizando Aplicações em Rails
-
Upload
juan-maiz -
Category
Technology
-
view
1.122 -
download
4
Transcript of Otimizando Aplicações em Rails
Otimizando Aplicações em Rails
essa palestra será rápida
ServidorActive Record / SQLRuby / RailsCacheBackgrounDRb MetalHTML, JS & CSS
Nginx, Apache
balanceadores de carga
joeandmotorboat.com/2008/02/28/apache-vs-nginx-web-server-performance-deathmatch/
Mongrel, Thin, Passenger
servidores de aplicação
http://izumi.plan99.net/blog/index.php/2008/03/31/benchmark-passenger-mod_rails-vs-mongrel-vs-thin/
Active Record / SQL
for list in List.all ... do some ... for contact in list.contacts ... do some ... endend
INCLUDE
for list in List.all(:include => :contacts) ... do some ... for contacts in list.contacts ... do some ... endend
INCLUDE
Rails não adiciona índices nas tabelasEXPLAIN ANALYZE SELECT * FROM contacts WHERE email = '[email protected]';>> cost=0.00..25.38CREATE INDEX contacts_email_idx ON contacts(email);>> cost=0.00..1.06
ÍNDICES
Newsletter#history>> Buscava dados de mais de 100 mensagens (total de envios, acessos, cliques, descadastros, informações geográficas...) em uma view e apresentava um gráfico.
Tempo médio do método: 18s CREATE TABLE messages_complete_history AS SELECT * FROM view_messages_complete_history;
Tempo médio do método: 0.3s >> Criação agendada para a madrugada com BackgroundRB
TABELAS CONSOLIDADAS
COPY (SELECT * FROM contacts) TO 'path' WITH DELIMITER ';' CSV HEADER
Muitas ordens de magnitude mais rápido do que AR + FasterCSV
CSV EXPORT
postgres_timestamps pluginsobrescreve comportamento de created_at e updated_at do ARcria defaults e triggers automaticamente nas migraçõesbenchmarks que fiz: de 2 a 5% mais rápido em 1000 inserts. até 10% mais rápido em 1000 updates;
>> Prometo para ainda este ano!
WORKING ON
Ruby / Rails
require 'benchmark' Benchmark.bm do |x|x.report('foo') { ... code ... }x.report('bar') { ... code ... }end
BENCHMARK
def calculate_something @something = do_some_heavy_calculation end
||=
def calculate_something(*args) do_some_heavy_calculationendmemoize :calculate_something calculate_something(1) #Hit calculate_something(1) #Cachecalculate_something(1) #Cache calculate_something(2) #Hit
MEMOIZE
# environment startup file require 'geoip_city'GEOIPDB = GeoIPCity::Database.new('file.dat') # some controller or modelGEOIPDB.look_up(ip)
RAILS STARTUP
ids = @contacts.map(&:id) #badids = @contacts.map{|c| c.id} #ugly, but good
str = str.gsub(/java/, 'ruby')#badstr.gsub!(/java/, 'ruby') #good
DICAS GERAIS
Cache
# site controllercaches_page :index
PAGE CACHE
# login controllerbefore_filter :get_clientcaches_action :index
ACTION CACHE
# some view- cache :action_suffix => 'comments' do = render :partial => 'comments' # no controller...def create_comment ... expire_fragment :action => 'show_post', :action_suffix => 'comments'end
FRAGMENT CACHE
# some view- cache :key => @contact.updated_at do = render :partial => 'contact_info'# Expira automático, mas gera alguns arquivos ...
FRAGMENT CACHE
fresh_when :last_modified => @product.published_at.utc, :etag => @article
HTTP CLIENT CACHE
http://github.com/nkallen/cache-money/tree/master
>> Try it out and tell us...
CACHE-MONEY PLUGIN
Metal
class Go def self.call(env) if env["PATH_INFO"] =~ /^\/go\/view\/(\d+)/ id = $1 request = Rack::Request.new(env) delivery = Delivery.find id View.create :contact_id => delivery.contact_id, :message_id => delivery.message_id, :ip => request.ip delivery.contact.confirm! [200, {"Content-Type" => "text/html"}, [""]] else [404, {"Content-Type" => "text/html"}, ["Not Found"]] end endend
METAL
2-5x faster>> NÃO DEVERIA SE CHAMAR SPEED METAL?
METAL
BackgrounDRb
Pode executar tarefas em bg;Pode agendar tarefas para executar em bg;Pode executar tarefas periodicamente em bg;Pode até conversar via TCP;
class ContactsWorker < BackgrounDRb::MetaWorker set_worker_name :contacts_worker def delete(args) total = args[:contacts].size cache[:total] = total cache[:curr] = 0 cache[:msg] = Contact.delete_many(args[:contacts]) do cache[:curr] += 1 end endend # start worker on controller MiddleMan.worker(:contacts_worker).async_delete(:args => {:contacts => @contacts}, :job_key => @job_key)
# verify worker status via Ajaxt = MiddleMan.worker(:contacts_worker).ask_result(:total)c = MiddleMan.worker(:contacts_worker).ask_result(:curr)return render :json => [t, c]
EXECUTANDO UMA TAREFA
Média antes: 13s (muitas FKs! milhares de contatos!)Média depois: 0.1s (usuário pode trabalhar durante processo)
EXECUTANDO UMA TAREFA
MiddleMan.worker(:messenger_worker).enq_send_messages( :args => @message.id, :scheduled_at => @message.scheduled_at, :job_key => "message_#{@message.id}")
EXECUTANDO UMA TAREFA AGENDADA
# backgroundrb.yml:schedules: :table_cache_worker: :table_cache: :trigger_args: 0 30 4 * * * *
EXECUTANDO UMA TAREFA PERIÓDICA
HTML, JS & CSS
CSS PLEASE DONT USE FONT TAG
SPRITES *JSMINGZIP
HTTP CLIENT CACHE (304!)return render :nothing => true, :status => 304
Cloud Storage e CDN
static.mailee.me