Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #12537 performance regression when preloading has_many_through association #19423

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -18,7 +18,8 @@ def associated_records_by_owner(preloader)
through_records = owners.map do |owner|
association = owner.association through_reflection.name

[owner, Array(association.reader)]
center = target_records_from_association(association)
[owner, Array(center)]
end

reset_association owners, through_reflection.name
Expand Down Expand Up @@ -49,7 +50,7 @@ def associated_records_by_owner(preloader)
rhs_records = middles.flat_map { |r|
association = r.association source_reflection.name

association.reader
target_records_from_association(association)
}.compact

rhs_records.sort_by { |rhs| record_offset[rhs] }
Expand Down Expand Up @@ -89,6 +90,10 @@ def through_scope

scope
end

def target_records_from_association(association)
association.loaded? ? association.target : association.reader
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yuroyoro why do you check against loaded? here? It seems that association is always loaded in this case and all tests remain green when I delete it. Can you add a test that fails without it?

end
end
end
end
Expand Down
6 changes: 6 additions & 0 deletions activerecord/test/cases/associations/eager_test.rb
Expand Up @@ -1349,4 +1349,10 @@ def test_deep_including_through_habtm
post = Post.eager_load(:tags).where('tags.name = ?', 'General').first
assert_equal posts(:welcome), post
end

# CollectionProxy#reader is expensive, so the preloader avoids calling it.
test "preloading has_many_through association avoids calling association.reader" do
ActiveRecord::Associations::HasManyAssociation.any_instance.expects(:reader).never
Author.preload(:readonly_comments).first!
end
end