Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

TDC2018SP | Trilha Ruby - Maior performance no seu sistema com o uso adequado de ORM em Rails

0 views

Published on

TDC2018SP | Trilha Ruby - Maior performance no seu sistema com o uso adequado de ORM em Rails

Published in: Education
  • Be the first to comment

  • Be the first to like this

TDC2018SP | Trilha Ruby - Maior performance no seu sistema com o uso adequado de ORM em Rails

  1. 1. Maior performance no seu sistema com o uso adequado de ORM em Rails
  2. 2. Isaac Felisberto de Souza Engenheiro de Software ...há “quase” 20 anos no mundo de desenvolvimento Já somos mais de 100 na área de desenvolvimento!
  3. 3. Vamos usar outra linguagem Ruby é lento Vamos fragmentar o sistema Precisamos reescrever do zero
  4. 4. Será que o problema é a linguagem ou o framework? Ou não estamos usando eles de forma adequada?
  5. 5. ORM EM RAILS
  6. 6. Object Relational Mapping
  7. 7. Rails nos oferece Active Record Ele é um Pattern! Martin Fowler
  8. 8. Order Customer Order Items Product Shipping Company Delivery
  9. 9. >Order.create(customer:...) >Order.find(1).customer.name >Order.find_by(customer:1).order_items.count >Order.all class Customer < ApplicationRecord end class Delivery < ApplicationRecord belongs_to :shipping_company belongs_to :ordem_item end class Order < ApplicationRecord belongs_to :customer has_many :order_items end class OrderItem < ApplicationRecord belongs_to :order belongs_to :product has_one :delivery end class Product < ApplicationRecord end class ShippingCompany < ApplicationRecord end Order Load (1.0ms) SELECT "orders".* FROM "orders" LIMIT $1 [["LIMIT", 11]] => #<ActiveRecord::Relation [#<Order id: 1, customer_id: 2, status: "ENTREGUE", ...]>
  10. 10. <table> <thead> <tr> <th>Id</th> <th>Status</th> </tr> </thead> <tbody> <% @orders.each do |order| %> <tr> <td><%= order.id %></td> <td><%= order.status %></td> </tr> <% end %> </tbody> </table>
  11. 11. <table> <thead> <tr> <th>Customer</th> <th>Status</th> </tr> </thead> <tbody> <% @orders.each do |order| %> <tr> <td><%= order.customer.name %></td> <td><%= order.status %></td> </tr> <% end %> </tbody> </table>
  12. 12. Para 10.000 orders, até 10.001 consultas ao banco (Queries N+1) def index @orders = Order.all end def index @orders = Order.all.includes(:customer) end 2 consultas ao banco de dados def index @orders = Order.all.joins(:customer).includes(:customer) end 1 consulta ao banco de dados
  13. 13. Default 16171ms 9408ms 9574ms 9783ms 17361ms 19387ms 13.6s Qual melhor estratégia? Include 1490ms 1440ms 1844ms 1581ms 1555ms 1969ms 1.6s Join + Include 2044ms 2168ms 1536ms 1903ms 1978ms 1668ms 1.8s + db index 1579ms 1653ms 1575ms 1556ms 1613ms 1564ms 1.5s
  14. 14. Como coletar essas métricas?
  15. 15. Uso do log do rails server
  16. 16. Application Performance Management (APM)
  17. 17. 15ms 1ms https://github.com/newrelic/rpm
  18. 18. Porque esses problemas ocorrem ?
  19. 19. SOLUÇÃO CRESCE ! ● FEATURES COMPLEXAS O TIME CRESCE !!! ● MAPEAMENTOS EQUIVOCADOS ● MAU USO DE NAVEGAÇÃO ENTRE OBJETOS ● MODELAGEM CONFUSA
  20. 20. Como evitar esses problemas ?
  21. 21. Bullet https://github.com/flyerhzm/bullet
  22. 22. DB Query Matchers https://github.com/brigade/db-query-matchers teste A teste B teste C teste D teste E teste F teste G teste H teste I teste J teste K teste L
  23. 23. require 'rails_helper' feature 'orders index' do scenario 'list all' do expect { visit('/orders') }.to make_database_queries(count: 3) end scenario 'list all' do expect { visit('/orders_include') }.to make_database_queries(count: 2) end scenario 'list all' do expect { visit('/orders_join') }.to make_database_queries(count: 1) end end
  24. 24. expect { }.to_not make_database_queries(matching: /UPDATE/) expect { }.to_not make_database_queries expect { }.to make_database_queries(count: 1) expect { }.to make_database_queries(count: 0..3) expect { }.to_not make_database_queries(manipulative: true) (INSERT, UPDATE, DELETE)
  25. 25. Boas Práticas
  26. 26. Uso de Scope def index_with_join @orders = Order.all.joins(:customer).includes(:customer) end Controller class Order < ApplicationRecord belongs_to :customer has_many :order_items scope :all_with_customer, -> {Order.all.joins(:customer).includes(:customer)} end Model def index_with_join @orders = Order.all_with_customer end Controller
  27. 27. Evite... scope :all_with_customer, -> { Order.all.includes(:customer).order(:status)} Ordenação default_scope :active, -> {Customer.where(active:true)}Default scope :closed, -> {Order.where(status:‘closed’) .where(‘total_amount >= ?’, 10.0)} Itens Implícitos scope :closed, -> do find_by_sql <<-SQL select * from orders where status = ‘closed’ SQL end SQL puro
  28. 28. Queries customizadas com Mapeamento Alternativos class SummaryOrder < ApplicationRecord self.table_name = "orders" scope :all_summarized, -> { select('orders.id') .select('customers.name') .select('orders.status') .select('count(order_items.id) as items_quantity') .joins('left join customers on orders.customer_id = customers.id') .joins('right join order_items on order_items.order_id = orders.id') .group('orders.id, customers.name, orders.status') } end
  29. 29. ... <tr> <th>Customer</th> <th>Status</th> <th>Quantity of Items</th> </tr> ... <% @summary_orders.each do |summary_order| %> <tr> <td><%= summary_order.name %></td> <td><%= summary_order.status %></td> <td><%= summary_order.items_quantity %></td> </tr> <% end %> ... class SummaryOrder attr_accessor :id, :name, :status, :items_quantity end
  30. 30. count, length ou size ? count length
  31. 31. size
  32. 32. OO ou SQL? Dê preferência ao uso de OO! OO não pode sacrificar performance ORM é um facilitador, conhecer SQL e banco de dados ainda é importante Não abra mão do poder do banco de dados
  33. 33. > SummaryOrder.all_summarized.explain HashAggregate (cost=871.09..971.09 rows=10000 width=57) Group Key: orders.id, customers.name -> Hash Left Join (cost=349.70..796.09 rows=10000 width=57) Hash Cond: (orders.customer_id = customers.id) -> Hash Left Join (cost=319.00..639.24 rows=10000 width=33) Hash Cond: (order_items.order_id = orders.id) -> Seq Scan on order_items (cost=0.00..194.00 rows=10000 width=16) -> Hash (cost=194.00..194.00 rows=10000 width=25) -> Seq Scan on orders (cost=0.00..194.00 rows=10000 width=25) -> Hash (cost=19.20..19.20 rows=920 width=40) -> Seq Scan on customers (cost=0.00..19.20 rows=920 width=40) Explain
  34. 34. Vimos resumidamente... Includes Joins Scopes Mapeamentos Alternativos New Relic Bullet DB Query Matchers Queries N+1 Count, Length, Size Explain
  35. 35. Isso resolve todos os problemas ? provavelmente não! Talvez ainda seja necessário... ● Reescrever ● Fragmentar ● Ou até usar outra linguagem
  36. 36. Faça pelos motivos adequados!
  37. 37. Verifique se o problema de performance não é mal uso de ORM
  38. 38. Obrigado ! Isaac Felisberto de Souza isaacsouza@gmail.com linkedin.com/in/isaacfsouza http://resultadosdigitais.com.br/ trabalhe-conosco/ Seja um RDoer! Dúvidas ?

×