Skip to content

Instantly share code, notes, and snippets.

@yuroyoro
Last active August 29, 2015 14:15
Show Gist options
  • Save yuroyoro/9a17e1b2064975249fc1 to your computer and use it in GitHub Desktop.
Save yuroyoro/9a17e1b2064975249fc1 to your computer and use it in GitHub Desktop.
Performance regression of ActiveRecord preloading :has_many through associations | Major performance regression when preloading has_many_through association · Issue #12537 · rails/rails https://github.com/rails/rails/issues/12537#issuecomment-55462750
begin
class CreateModels < ActiveRecord::Migration
def change
create_table :items do |t|
end
create_table :events do |t|
t.belongs_to :item
end
create_table :event_people do |t|
t.belongs_to :event
t.belongs_to :person
end
create_table :people do |t|
end
end
end
class Item < ActiveRecord::Base
has_many :events
has_many :event_people, :through => :events
has_many :people, :through => :events
end
class Event < ActiveRecord::Base
belongs_to :item
has_many :event_people
has_many :people, :through => :event_people
end
class EventPerson < ActiveRecord::Base
belongs_to :event
belongs_to :person
end
class Person < ActiveRecord::Base
end
CreateModels.new.change
puts "Populating Database"
1000.times do
Item.create
end
Item.all.each_with_index do |item|
event = Event.new
event.people.build
event.people.build
item.events << event
end
rescue => e
# Tables already exist
puts e.message
ensure
require 'benchmark'
Benchmark.benchmark("#{Rails.version} - #{ActiveRecord::Base.configurations[Rails.env]['adapter']}\n", 40) do |x|
x.report('limit 100 has_many') { 1.times { Item.preload(:events).limit(100).order(:id).to_a } }
x.report('limit 100 has_many_through') { 1.times { Item.preload(:event_people).limit(100).order(:id).to_a } }
x.report('limit 100 double has_many_through') { 1.times { Item.preload(:people).limit(100).order(:id).to_a } }
x.report('limit 1000 has_many') { 1.times { Item.preload(:events).limit(1000).order(:id).to_a } }
x.report('limit 1000 has_many_through') { 1.times { Item.preload(:event_people).limit(1000).order(:id).to_a } }
x.report('limit 1000 double has_many_through') { 1.times { Item.preload(:people).limit(1000).order(:id).to_a } }
end
end
$ ./run_bench.sh
--- Rails 3.2
3.2.21 - postgresql
limit 100 has_many 0.030000 0.000000 0.030000 ( 0.044157)
limit 100 has_many_through 0.020000 0.010000 0.030000 ( 0.027665)
limit 100 double has_many_through 0.040000 0.000000 0.040000 ( 0.037460)
limit 1000 has_many 0.050000 0.000000 0.050000 ( 0.065530)
limit 1000 has_many_through 0.140000 0.000000 0.140000 ( 0.152469)
limit 1000 double has_many_through 0.240000 0.000000 0.240000 ( 0.267607)
--- Rails 4.1
4.1.8 - postgresql
limit 100 has_many 0.020000 0.020000 0.040000 ( 0.045980)
limit 100 has_many_through 0.070000 0.020000 0.090000 ( 0.086634)
limit 100 double has_many_through 0.100000 0.000000 0.100000 ( 0.114789)
limit 1000 has_many 0.060000 0.000000 0.060000 ( 0.066844)
limit 1000 has_many_through 0.540000 0.020000 0.560000 ( 0.573936)
limit 1000 double has_many_through 0.910000 0.070000 0.980000 ( 0.997398)
--- Rails 4.2
4.2.0 - postgresql
limit 100 has_many 0.040000 0.030000 0.070000 ( 0.068367)
limit 100 has_many_through 0.090000 0.020000 0.110000 ( 0.112198)
limit 100 double has_many_through 0.120000 0.010000 0.130000 ( 0.138451)
limit 1000 has_many 0.070000 0.010000 0.080000 ( 0.081651)
limit 1000 has_many_through 0.610000 0.020000 0.630000 ( 0.644392)
limit 1000 double has_many_through 1.080000 0.080000 1.160000 ( 1.180719)
--- Rails 4.2 edge
4.2.0 - postgresql
limit 100 has_many 0.030000 0.010000 0.040000 ( 0.042943)
limit 100 has_many_through 0.060000 0.010000 0.070000 ( 0.074245)
limit 100 double has_many_through 0.100000 0.010000 0.110000 ( 0.109340)
limit 1000 has_many 0.060000 0.000000 0.060000 ( 0.072432)
limit 1000 has_many_through 0.520000 0.030000 0.550000 ( 0.563653)
limit 1000 double has_many_through 0.900000 0.060000 0.960000 ( 0.980529)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment