Last active
July 21, 2017 13:35
-
-
Save boykoc/6514cd4773ebdd9ad49195bc34768453 to your computer and use it in GitHub Desktop.
Eager loading join causing n+1-1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Create rails app to see issue. | |
rails new sample | |
rails g scaffold Author name:string | |
rails g scaffold Post title:string body:text author:references | |
rails g scaffold Comment body:text post:references | |
# Update post model to add association. | |
has_many :comments | |
# Update seed.rb | |
authors = Author.create([{name: 'User 1'}, {name: 'User 2'}]) | |
posts = Post.create([{title: 'post 1', body: 'body of post 1', author: authors.first}, {title: 'post 2', body: 'body of post 2', author: authors.first}, {title: 'post 3', body: 'body of post 3', author: authors.second}]) | |
comments = Comment.create([{body: 'comment 1', post: posts.first}, {body: 'comment 2', post: posts.first}, {body: 'comment 3', post: posts.first}, {body: 'comment 4', post: posts.second}, {body: 'comment 5', post: posts.third}, {body: 'comment 6', post: posts.third}, {body: 'comment 7', post: posts.third}, {body: 'comment 8', post: posts.third}]) | |
# Run migrations and seed database. | |
rake db:migrate | |
rake db:seed | |
# Go to console to test output. | |
rails c | |
posts = Post.joins(:comments).select("posts.*, comments.body").includes(:author) | |
posts.each do |x| | |
puts "#{x.title} #{x.author.name} #{x.body}" | |
end | |
# Console output. | |
# post 1 User 1 comment 1 | |
# Author Load (0.3ms) SELECT "authors".* FROM "authors" WHERE "authors"."id" = ? LIMIT 1 [["id", 1]] | |
# post 1 User 1 comment 2 | |
# Author Load (0.1ms) SELECT "authors".* FROM "authors" WHERE "authors"."id" = ? LIMIT 1 [["id", 1]] | |
# post 1 User 1 comment 3 | |
# post 2 User 1 comment 4 | |
# post 3 User 2 comment 5 | |
# Author Load (0.1ms) SELECT "authors".* FROM "authors" WHERE "authors"."id" = ? LIMIT 1 [["id", 2]] | |
# post 3 User 2 comment 6 | |
# Author Load (0.1ms) SELECT "authors".* FROM "authors" WHERE "authors"."id" = ? LIMIT 1 [["id", 2]] | |
# post 3 User 2 comment 7 | |
# Author Load (0.1ms) SELECT "authors".* FROM "authors" WHERE "authors"."id" = ? LIMIT 1 [["id", 2]] | |
# post 3 User 2 comment 8 | |
posts = Post.joins(:comments).select("posts.*, comments.body").includes(:author) | |
posts.each do |x| | |
puts x.association(:author).loaded? | |
end | |
# Output. | |
# true | |
# false | |
# false | |
# true | |
# true | |
# false | |
# false | |
# false | |
# Interestingly the following test does not fail. | |
class PostTest < ActiveSupport::TestCase | |
test "should eager load author for each post" do | |
posts = Post.joins(:comments).select("posts.*, comments.body").includes(:author) | |
posts.each do |x| | |
assert x.association(:author).loaded? | |
# Also assert x.author.loaded? passes but errors in the console. | |
end | |
end | |
end | |
# Test output. | |
# Running via Spring preloader in process 2614 | |
# Run options: --seed 29018 | |
# # Running: | |
# ...................... | |
# Finished in 1.561278s, 14.0910 runs/s, 24.9795 assertions/s. | |
# 22 runs, 39 assertions, 0 failures, 0 errors, 0 skips | |
############################################################## | |
# This only eager loads the Author for the first instance of each post. | |
# Each consecutive instance of a post triggers an additional query to find their authors. | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment