mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Only preload misses on multifetch cache
This commit is contained in:
parent
a3b7aa66bc
commit
d04a32fe67
6 changed files with 101 additions and 9 deletions
35
actionview/test/activerecord/multifetch_cache_test.rb
Normal file
35
actionview/test/activerecord/multifetch_cache_test.rb
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "active_record_unit"
|
||||||
|
require "active_record/railties/collection_cache_association_loading"
|
||||||
|
|
||||||
|
ActionView::PartialRenderer.prepend(ActiveRecord::Railties::CollectionCacheAssociationLoading)
|
||||||
|
|
||||||
|
class MultifetchCacheTest < ActiveRecordTestCase
|
||||||
|
fixtures :topics, :replies
|
||||||
|
|
||||||
|
def setup
|
||||||
|
view_paths = ActionController::Base.view_paths
|
||||||
|
|
||||||
|
@view = Class.new(ActionView::Base) do
|
||||||
|
def view_cache_dependencies
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
|
def combined_fragment_cache_key(key)
|
||||||
|
[ :views, key ]
|
||||||
|
end
|
||||||
|
end.new(view_paths, {})
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_only_preloading_for_records_that_miss_the_cache
|
||||||
|
@view.render partial: "test/partial", collection: [topics(:rails)], cached: true
|
||||||
|
|
||||||
|
@topics = Topic.preload(:replies)
|
||||||
|
|
||||||
|
@view.render partial: "test/partial", collection: @topics, cached: true
|
||||||
|
|
||||||
|
assert_not @topics.detect { |topic| topic.id == topics(:rails).id }.replies.loaded?
|
||||||
|
assert @topics.detect { |topic| topic.id != topics(:rails).id }.replies.loaded?
|
||||||
|
end
|
||||||
|
end
|
|
@ -157,6 +157,14 @@ end_warning
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
initializer "active_record.collection_cache_association_loading" do
|
||||||
|
require "active_record/railties/collection_cache_association_loading"
|
||||||
|
ActiveSupport.on_load(:action_view) do
|
||||||
|
ActionView::PartialRenderer.prepend(ActiveRecord::Railties::CollectionCacheAssociationLoading)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
initializer "active_record.set_reloader_hooks" do
|
initializer "active_record.set_reloader_hooks" do
|
||||||
ActiveSupport.on_load(:active_record) do
|
ActiveSupport.on_load(:active_record) do
|
||||||
ActiveSupport::Reloader.before_class_unload do
|
ActiveSupport::Reloader.before_class_unload do
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module ActiveRecord
|
||||||
|
module Railties # :nodoc:
|
||||||
|
module CollectionCacheAssociationLoading #:nodoc:
|
||||||
|
def setup(context, options, block)
|
||||||
|
@relation = relation_from_options(options)
|
||||||
|
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
def relation_from_options(cached: nil, partial: nil, collection: nil, **_)
|
||||||
|
return unless cached
|
||||||
|
|
||||||
|
relation = partial if partial.is_a?(ActiveRecord::Relation)
|
||||||
|
relation ||= collection if collection.is_a?(ActiveRecord::Relation)
|
||||||
|
|
||||||
|
if relation && !relation.loaded?
|
||||||
|
relation.skip_preloading!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def collection_without_template
|
||||||
|
@relation.preload_associations(@collection) if @relation
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
def collection_with_template
|
||||||
|
@relation.preload_associations(@collection) if @relation
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -8,7 +8,8 @@ module ActiveRecord
|
||||||
:extending, :unscope]
|
:extending, :unscope]
|
||||||
|
|
||||||
SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering,
|
SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering,
|
||||||
:reverse_order, :distinct, :create_with, :skip_query_cache]
|
:reverse_order, :distinct, :create_with, :skip_query_cache,
|
||||||
|
:skip_preloading]
|
||||||
CLAUSE_METHODS = [:where, :having, :from]
|
CLAUSE_METHODS = [:where, :having, :from]
|
||||||
INVALID_METHODS_FOR_DELETE_ALL = [:distinct, :group, :having]
|
INVALID_METHODS_FOR_DELETE_ALL = [:distinct, :group, :having]
|
||||||
|
|
||||||
|
@ -546,6 +547,16 @@ module ActiveRecord
|
||||||
ActiveRecord::Associations::AliasTracker.create(connection, table.name, joins)
|
ActiveRecord::Associations::AliasTracker.create(connection, table.name, joins)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def preload_associations(records)
|
||||||
|
preload = preload_values
|
||||||
|
preload += includes_values unless eager_loading?
|
||||||
|
preloader = nil
|
||||||
|
preload.each do |associations|
|
||||||
|
preloader ||= build_preloader
|
||||||
|
preloader.preload records, associations
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def load_records(records)
|
def load_records(records)
|
||||||
|
@ -575,13 +586,7 @@ module ActiveRecord
|
||||||
klass.find_by_sql(arel, &block).freeze
|
klass.find_by_sql(arel, &block).freeze
|
||||||
end
|
end
|
||||||
|
|
||||||
preload = preload_values
|
preload_associations(@records) unless skip_preloading_value
|
||||||
preload += includes_values unless eager_loading?
|
|
||||||
preloader = nil
|
|
||||||
preload.each do |associations|
|
|
||||||
preloader ||= build_preloader
|
|
||||||
preloader.preload @records, associations
|
|
||||||
end
|
|
||||||
|
|
||||||
@records.each(&:readonly!) if readonly_value
|
@records.each(&:readonly!) if readonly_value
|
||||||
|
|
||||||
|
|
|
@ -899,6 +899,11 @@ module ActiveRecord
|
||||||
self
|
self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def skip_preloading! # :nodoc:
|
||||||
|
self.skip_preloading_value = true
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
# Returns the Arel object associated with the relation.
|
# Returns the Arel object associated with the relation.
|
||||||
def arel(aliases = nil) # :nodoc:
|
def arel(aliases = nil) # :nodoc:
|
||||||
@arel ||= build_arel(aliases)
|
@arel ||= build_arel(aliases)
|
||||||
|
|
|
@ -59,7 +59,7 @@ module ActiveRecord
|
||||||
assert_equal [], relation.extending_values
|
assert_equal [], relation.extending_values
|
||||||
end
|
end
|
||||||
|
|
||||||
(Relation::SINGLE_VALUE_METHODS - [:lock, :reordering, :reverse_order, :create_with, :skip_query_cache]).each do |method|
|
(Relation::SINGLE_VALUE_METHODS - [:lock, :reordering, :reverse_order, :create_with, :skip_query_cache, :skip_preloading]).each do |method|
|
||||||
test "##{method}!" do
|
test "##{method}!" do
|
||||||
assert relation.public_send("#{method}!", :foo).equal?(relation)
|
assert relation.public_send("#{method}!", :foo).equal?(relation)
|
||||||
assert_equal :foo, relation.public_send("#{method}_value")
|
assert_equal :foo, relation.public_send("#{method}_value")
|
||||||
|
@ -137,6 +137,11 @@ module ActiveRecord
|
||||||
assert relation.skip_query_cache_value
|
assert relation.skip_query_cache_value
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "skip_preloading!" do
|
||||||
|
relation.skip_preloading!
|
||||||
|
assert relation.skip_preloading_value
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def relation
|
def relation
|
||||||
@relation ||= Relation.new(FakeKlass)
|
@relation ||= Relation.new(FakeKlass)
|
||||||
|
|
Loading…
Reference in a new issue