mirror of https://github.com/rails/rails
Strict loading using `:n_plus_one_only` does not eagerly load child associations.
Before: ```ruby person = Person.find(1) person.strict_loading!(mode: :n_plus_one_only) person.posts.first # SELECT * FROM posts WHERE person_id = 1; -- non-deterministic order ``` After: ```ruby person = Person.find(1) person.strict_loading!(mode: :n_plus_one_only) person.posts.first # this is 1+1, not N+1 # SELECT * FROM posts WHERE person_id = 1 ORDER BY id LIMIT 1; ``` Strict loading in `:n_plus_one_only` mode is designed to prevent performance issues when deeply traversing associations. It allows `Person.find(1).posts`, but _not_ `Person.find(1).posts.map(&:category)`. With this change, child associations are no longer eagerly loaded, to match intended behavior and to prevent non-deterministic order issues caused by calling methods like `first` or `last`. Fixes #49473.
This commit is contained in:
parent
d60a23457c
commit
715276071f
|
@ -1,3 +1,33 @@
|
|||
* Strict loading using `:n_plus_one_only` does not eagerly load child associations.
|
||||
|
||||
With this change, child associations are no longer eagerly loaded, to
|
||||
match intended behavior and to prevent non-deterministic order issues caused
|
||||
by calling methods like `first` or `last`. As `first` and `last` don't cause
|
||||
an N+1 by themselves, calling child associations will no longer raise.
|
||||
Fixes #49473.
|
||||
|
||||
Before:
|
||||
|
||||
```ruby
|
||||
person = Person.find(1)
|
||||
person.strict_loading!(mode: :n_plus_one_only)
|
||||
person.posts.first
|
||||
# SELECT * FROM posts WHERE person_id = 1; -- non-deterministic order
|
||||
person.posts.first.firm # raises ActiveRecord::StrictLoadingViolationError
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```ruby
|
||||
person = Person.find(1)
|
||||
person.strict_loading!(mode: :n_plus_one_only)
|
||||
person.posts.first # this is 1+1, not N+1
|
||||
# SELECT * FROM posts WHERE person_id = 1 ORDER BY id LIMIT 1;
|
||||
person.posts.first.firm # no longer raises
|
||||
```
|
||||
|
||||
*Reid Lynch*
|
||||
|
||||
* Allow `Sqlite3Adapter` to use `sqlite3` gem version `2.x`
|
||||
|
||||
*Mike Dalessio*
|
||||
|
|
|
@ -303,7 +303,7 @@ module ActiveRecord
|
|||
|
||||
def find_from_target?
|
||||
loaded? ||
|
||||
owner.strict_loading? ||
|
||||
(owner.strict_loading? && owner.strict_loading_all?) ||
|
||||
reflection.strict_loading? ||
|
||||
owner.new_record? ||
|
||||
target.any? { |record| record.new_record? || record.changed? }
|
||||
|
|
|
@ -704,6 +704,11 @@ module ActiveRecord
|
|||
@strict_loading_mode == :n_plus_one_only
|
||||
end
|
||||
|
||||
# Returns +true+ if the record uses strict_loading with +:all+ mode enabled.
|
||||
def strict_loading_all?
|
||||
@strict_loading_mode == :all
|
||||
end
|
||||
|
||||
# Marks this record as read only.
|
||||
#
|
||||
# customer = Customer.first
|
||||
|
|
|
@ -86,6 +86,18 @@ class StrictLoadingTest < ActiveRecord::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_strict_loading_n_plus_one_only_mode_does_not_eager_load_child_associations
|
||||
developer = Developer.first
|
||||
developer.strict_loading!(mode: :n_plus_one_only)
|
||||
developer.projects.first
|
||||
|
||||
assert_not_predicate developer.projects, :loaded?
|
||||
|
||||
assert_nothing_raised do
|
||||
developer.projects.first.firm
|
||||
end
|
||||
end
|
||||
|
||||
def test_strict_loading
|
||||
Developer.all.each { |d| assert_not d.strict_loading? }
|
||||
Developer.strict_loading.each { |d| assert_predicate d, :strict_loading? }
|
||||
|
|
Loading…
Reference in New Issue