speeding up rails

ruby on rails langzamer

  • Benchmarks in vergelijking met andere talen
  • Interpreted vs. compiled language
  • Garbage collection
  • Langzamere method calls

Oorzaken

  • Meestal database queries
  • Teveel data inladen
  • N+1 queries
  • Te weinig cached waardes
  • Te weinig database queries

Lijkt snel

  • Applicatie kan snel zijn in het begin
  • Steeds meer database entries
  • Groot verschil

Existence checks

  • Worden heel vaak gebruikt
  • Verschillende functies
  • present?
  • empty?
  • any?
  • exists?
  • Andere tel-gebaseerde manieren

Present?

Build.where(:created_at => 7.days.ago..1.day.ago).passed.present?

SELECT "builds".* FROM "builds" WHERE ("builds"."created_at" BETWEEN
2017-02-22 21:22:27.133402' AND '2017-02-28 21:22:27.133529') AND
"builds"."result" = $1 [["result", "passed"]]

 

  • Laadt alle records in
  • Omzetten naar ActiveRecord objecten
  • Checken of array leeg is

any? & empty?

Build.where(:created_at => 7.days.ago..1.day.ago).passed.any?
Build.where(:created_at => 7.days.ago..1.day.ago).passed.empty?

SELECT COUNT(*) FROM "builds" WHERE ("builds"."created_at" BETWEEN
'2017-02-22 21:22:16.885942' AND '2017-02-28 21:22:16.886077') AND
"builds"."result" = $1 [["result", "passed"]]

 

  • Geoptimaliseerd in Rails
  • Count query is efficiënt

Exists?

Build.where(:created_at => 7.days.ago..1.day.ago).passed.exists?

SELECT 1 AS one FROM "builds" WHERE ("builds"."created_at" BETWEEN
'2017-02-22 21:23:04.066301' AND '2017-02-28 21:23:04.066443') AND
"builds"."result" = $1 LIMIT 1 [["result", "passed"]]

 

  • Nog meer geoptimaliseerd
  • Beste keuze
  • SELECT 1...LIMIT 1

Performance

present? =>  2892.7 ms
any?     =>   400.9 ms
empty?   =>   403.9 ms
exists?  =>     1.1 ms

 

Soms 400 keer sneller

200ms is acceptabel

Altijd exists? gebruiken

  • Meestal de beste performance
  • Any? is sneller als de records in het geheugen zitten

 

project = Project.find_by_name('ivaldi')

project.builds.load

project.builds.any?    # no database hit
project.builds.exists? # hits the database

DATABASE indexes

add_index :projects, :name
  • Record ophalen a.d.h.v. bepaalde column
  • Bijvoorbeeld column 'name'
  • Standaard wordt elk record 1 voor 1 nagelopen, tot een match 

Vuistregel

  • Alles wat gebruikt wordt in de volgende gedeeltes van queries
WHERE, HAVING, ORDER BY

 

Bijvoorbeeld:

Project.find_by(name: 'Ivaldi')

Test

  • 10.000 projects met index (name), 10.000 companies zonder
  • Beiden zelfde ID
Benchmark.ms { Project.find_by_name('Tremayne Cummerata') }
# 0.5210000090301037

Benchmark.ms { Company.find_by_name('Mrs. Elvie Thompson') }
# 0.9719999507069588

Index foreign keys

  • belongs_to of has_many relaties
  • Let op bij polymorphic relaties

 

# Maakt geen verschil
add_index :projects, :owner_id
add_index :projects, :owner_type

# Wordt wel sneller
add_index :projects, [:owner_id, :owner_type]

Ordered records

  • De column waarop vaak gesorteerd wordt
Project.order(:name)
add_index :name

test

Benchmark.ms { Project.order(:name) }
# 0.07900013588368893

Benchmark.ms { Company.order(:name) }
# 0.10000006295740604

Altijd indexes gebruiken?

  • Performance kan sterk verbeterd worden
  • Soms kan het geen nut hebben (of langzamer maken)
  • Tabellen met veel verwijderde records of velden
  • Hele grote tabellen nemen meer ruimte in beslag voor de indexes
Made with Slides.com